immer.js

내승현·2023년 10월 17일
0

Redux

목록 보기
3/4

immer.js란 ?

react에서 불변성을 유지하는 코드를 작성하기 쉽게 해주는 라이브러리

불변성이란?

쉽게 말하면 상태를 변경하지 않는 것이다.
상태를 변경하는데, 상태를 변경하지 않으면서 원하는 상태를 바꾼다는게 모순적인 거 같지만 ...

React 기본 속성

react는 기본적으로 부모 컴포넌트가 리렌더링을 하면 자식 컴포넌트도 리렌더링하게 된다. 즉, 얕은 비교를 통해 새로운 값인지 아닌지를 판단한 후 새로운 값인 경우 리렌더링을 하게 된다.
여기서 얕은 비교란 ! 객체 , 배열, 함수와 같은 참조 타입들을 실제 내부 값까지 비교하지 않고 동일 참조인지(동일한 메모리 값을 사용하는지)를 비교하는 것을 뜻합니다.

  1. 우리가 컴포넌트를 리렌더링 해야하는 상황이 있다고 가정하면 , 타입이 배열인 state를 바꾼다.
  2. 이때 , state.push(1)을 통해 state 배열에 직접 접근하여 요소를 추가한다.
  3. 우리는 push 전과 다른 값이라고 생각하지만, 리액트는 state라는 값은 새로운 참조값이 아니기 때문에 이전과 같은 값이라고 인식하고 리렌더링 하지 않는다.
  • 즉, 위 이유로 우리가 state를 바꾸고 돔을 다시 만들려면, 새로운 객체 or 배열을 만들어 새로운 참조값을 만들고, react에게 이 값은 이전과 다른 참조값임을 알려야하는 것이다.
    (위 과정은 가상 dom에서만 이뤄지는 렌더링)

불변성 지키면서 state 바꾸기

배열에 추가

setUsers(state.array.concat(user));

배열에서 삭제

const onRemove = id => {
  // user.id 가 id 인 것을 제거
  setUsers(users.filter(user => user.id !== id));
};

배열에서 수정

const onToggle = id => {
  setUsers(
    users.map(user =>
      user.id === id ? { ...user, active: !user.active } : user
    )
  );
};
 

객체에서 추가

setState(state => {...state, key: value})

객체에서 제거

setState(state => {..._.omit(state, 'deleteKey')})

객체에서 수정

setState(state => {...state, key: newValue})
  • 위 처럼 배열 혹은 객체를 바꾸면 불변성을 유지하면서 변경할 수 있다.
    그러나 immer.js를 이용한 일반 객체 또는 배열 다루듯 사용하면 immer가 불변성을 지켜준다!

immer

import produce from "immer";

const baseState = [
  {
    todo: "Learn typescript",
    done: true
  },
  {
    todo: "Try immer",
    done: false
  }
];

const nextState = produce(baseState, draftState => {
  draftState.push({ todo: "Tweet about it" });
  draftState[1].done = true;
});

immer에서 우리가 쓸 함수는 오직 produce만 알면 된다. 2가지의 파람을 가져오고 첫번째는 수정하고 싶은 객체/배열 , 두번째는 첫번째 파라미터에 할당된 객체/배열을 바꾸는 함수이다.


redux에서 immer 쓰기

const initialState = [{ name: "nkh", address: { city: "seoul" } }];

export default function auth(state = initialState, action) {
  produce(state, draft => {
    switch (action.type) {
      case SET_INFO:
        draft[0].name = action.data.name;
        draft[0].address.city = action.data.city;
        break;
      case ADD_INFO:
        draft.push({ name: "hhh", address: { city: "zzz" } });
      default:
        return draft;
    }
  });
}
 
  • 공식 예제
const byId = (state, action) =>
  produce(state, draft => {
    switch (action.type) {
      case RECEIVE_PRODUCTS:
        action.products.forEach(product => {
          draft[product.id] = product;
        });
        break;
    }
  });

const byId = (state, action) => {
  switch (action.type) {
    case RECEIVE_PRODUCTS:
      return {
        ...state,
        ...action.products.reduce((obj, product) => {
          obj[product.id] = product;
          return obj;
        }, {})
      };
    default:
      return state;
  }
};
profile
아토언니의 프론트엔드 개발자로서의 기록

0개의 댓글