📌 리듀서 함수내에서는 사이드 이펙트가 없고 동기식이여야 합니다.
따라서, 비동기식 코드는 리듀서 함수에 들어가면 안됩니다.
⭕️ useEffect 내에 모두 작성할 수 있지만 많은 것들을 컴포넌트안에 작성하는 것보단
분리해서 작성하는 것이 컴포넌트를 린상태로 깔끔하게 유지할 수 있다.
useEffect(() => {
if (isIntial) {
isIntial = false;
return;
}
dispatch(sendCartData(cart));
⭐️
// 함수를 반환하는 것도 허용한다. (리덕스툴킷을 사용한다면 가능)
// 함수작업을 디스패치 하는 것으로 확인되면 해당 함수를 자동으로 실행합니다.
// 그리고 디스패치를 인수를 준다.
}, [cart, dispatch]);
export const sendCartData = (cart) => {
return async (dispatch) => { // ⭐️ dispatch를 인수로 받음
dispatch(
uiActions.showNotification({
status: 'pending',
title: 'Sending...',
message: 'Sending cart data!',
})
);
const sendRequest = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/todos/1', {
method: 'PUT',
body: JSON.stringify(cart),
});
if (!res.ok) {
throw new Error('Sending cart data failed.');
}
};
try {
await sendRequest();
dispatch(
uiActions.showNotification({
status: 'success',
title: 'Success...',
message: 'Sent cart data successfully!',
})
);
} catch {
dispatch(
uiActions.showNotification({
status: 'error',
title: 'Error!',
message: 'Sending cart data failed!',
})
);
}
};
};
index.js
import { Provider } from 'react-redux';
import { store } from './store/index';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
store.js
import { createSlice, configureStore } from '@reduxjs/toolkit';
const initialCounterState = { counter: 0, isVisible: false };
const counterSlice = createSlice({
name: 'counter',
initialState: initialCounterState,
reducers: {
increase(state, action) {
state.counter = state.counter + action.payload; // payload란 이름은 정해져있음
},
decrease(state, action) {
state.counter = state.counter - action.payload;
},
isVisible(state) {
state.isVisible = !state.isVisible;
},
},
});
const initialAuthState = { isAuth: false };
const authSlice = createSlice({
name: 'auth',
initialState: initialAuthState,
reducers: {
login(state) {
state.isAuth = true;
},
logout(state) {
state.isAuth = false;
},
},
});
// ⭐️ 여러개 리듀서 설정
export const store = configureStore({
reducer: { counter: counterSlice.reducer, auth: authSlice.reducer },
});
export const counterActions = counterSlice.actions;
export const authActions = authSlice.actions;
counter.js
import { useSelector, useDispatch } from 'react-redux';
import { counterActions } from '../store/index';
const Counter = () => {
const { counter, isVisible } = useSelector((state) => state.counter)
// ⭐️ 여러개의 리듀서가 있을 경우 해당하는 reducer 접근을 위해 키값으로 접근
const dispatch = useDispatch();
const toggleCounterHandler = () => {
dispatch(counterActions.isVisible());
};
return (
<main className={classes.counter}>
<h1>Redux Counter</h1>
{isVisible && <div className={classes.value}>{counter}</div>}
<div>
<button onClick={() => dispatch(counterActions.increase(5))}>increase</button> // action.payload = 5;
<button onClick={() => dispatch(counterActions.decrease(3))}>decrease</button> // action.payload = 3;
</div>
<button onClick={toggleCounterHandler}>Toggle Counter</button>
</main>
);
};
export default Counter;