redux-toolkit을 공부하면서 이전에 redux를 이용해 만들었었던 프로젝트를 redux-toolkit으로 리팩토링해보았다.
아이템을 장바구니에 추가하면 알림이 떴다 사라지는 기능을 구현 중에 위와 같은 에러가 나타났다.
// 장바구니 추가 버튼 클릭시
const handleClick = (item) => {
if (!cartItems.some((v) => v.itemId === item.id)) {
dispatch(addToCart(item));
dispatch(notify(`장바구니에 ${item.name}이(가) 추가되었습니다.`));
} else {
dispatch(notify("이미 추가된 상품입니다."));
}
};
// 알림 관련 슬라이스
const notificationSlice = createSlice({
name: "notification",
initialState: initialState.notifications,
reducers: {
notify: (state, action) => {
const dismissTime = 5000;
const notification = {
message: action.payload,
dismissTime,
uuid: Math.random(),
};
state.push(notification);
setTimeout(() => {
state = state.slice(1);
}, dismissTime);
},
},
});
문제가 발생한 이유: 리듀서 함수는 순수함수여야 한다.
1. uuid를 Math.random()로 생성함.
2. setTimeout으로 비동기적 함수실행.
이 문제는 reducer 함수에 side effect를 발생시키는 동작이 포함되었기 때문에 발생하였다. reducer에는 다음의 규칙들이 적용되어야 한다.
state
와 action
를 기반으로 새 state 값만 계산해야 한다.💡 side effect는 함수에서 값을 반환하는 것 외에 상태나 동작이 변경되는 것을 말한다. side effect의 종류에는 다음과 같은 것들이 있다.
- console.log
- 파일 저장
- 비동기 타이머 설정
- AJAX HTTP 요청 만들기
- 함수 외부에 있는 상태를 수정하거나, 함수를 인자로 전달하는 것
- 난수 또는 고유한 임의 ID 생성(Math.random(), Date.now() 등)
변경 후 코드
const handleClick = (item) => {
const notification = {
dismissTime: 5000,
uuid: Math.random(),
message: `장바구니에 ${item.name}이(가) 추가되었습니다.`,
};
if (!cartItems.some((v) => v.itemId === item.id)) {
dispatch(addToCart(item));
} else {
notification.message = "이미 추가된 상품입니다.";
}
dispatch(enqueueNotification(notification));
setTimeout(() => {
dispatch(dequeueNotification());
});
};
const notificationSlice = createSlice({
name: "notification",
initialState: initialState,
reducers: {
enqueueNotification: (state, action) => {
state.notifications.push(action.payload);
},
dequeueNotification: (state, action) => {
state.notifications = state.notifications.slice(1);
},
},
});
https://redux.js.org/tutorials/fundamentals/part-3-state-actions-reducers#handling-actions