@reduxjs/toolkit: Cannot perform 'get' on a proxy that has been revoked 에러(reducer 규칙 지키기)

이예빈·2022년 10월 21일
1

JavaScript

목록 보기
25/26
post-thumbnail

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에는 다음의 규칙들이 적용되어야 한다.

  1. 인자로 주어지는 stateaction를 기반으로 새 state 값만 계산해야 한다.
  2. 기존의 state를 수정할 수 없고, 복사한 값을 변경해야 한다.
  3. 비동기 로직 또는 side effect가 발생해서는 안된다.

💡 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);
    },
  },
});

reference


https://redux.js.org/tutorials/fundamentals/part-3-state-actions-reducers#handling-actions

profile
temporary potato

0개의 댓글