221230 항해99 54일차 Immer

요니링 컴터 공부즁·2023년 1월 9일
0
post-custom-banner
  • 리액트에서 배열이나 객체를 업데이트 해야 할 때에는 직접 수정하면 안되고, 불변성을 지켜주면서 업데이트 해주어야 한다.
  • 대부분의 경우 ... 연산자 또는 배열 내장함수를 사용하는게 그렇게 어렵지는 않지만, 데이터의 구조가 까다로워지면 불변성을 지키며 새로운 데이터를 생성해내는 코드가 복잡해진다.
const state = {
  posts: [
    {
      id: 1,
      title: '제목입니다.',
      body: '내용입니다.',
      comments: [
        {
          id: 1,
          text: '와 정말 잘 읽었습니다.'
        }
      ]
    },
    {
      id: 2,
      title: '제목입니다.',
      body: '내용입니다.',
      comments: [
        {
          id: 2,
          text: '또 다른 댓글 어쩌고 저쩌고'
        }
      ]
    }
  ],
  selectedId: 1
};
  • 위 코드에서 posts 배열 안의 id1post 객체를 찾아서, comments에 새로운 댓글 객체를 추가해줘야한다고 가정하면, 다음과 같이 업데이트 해줘야 할 것이다.
const nextState = {
  ...state,
  posts: state.posts.map(post =>
    post.id === 1
      ? {
          ...post,
          comments: post.comments.concat({
            id: 3,
            text: '새로운 댓글'
          })
        }
      : post
  )
};
  • immer 라이브러리를 사용하면 다음과 같이 구현할 수 있다.
const nextState = produce(state, draft => {
  const post = draft.posts.find(post => post.id === 1);
  post.comments.push({
    id: 3,
    text: '와 정말 쉽다!'
  });
});
  • Immer는 이처럼 상태를 업데이트할 때, 불변성을 신경쓰지 않으면서 업데이트 해주면 알아서 불변성 관리를 대신 해준다.

Immer 사용법

1) Immer 설치

$ yarn add immer

2) 코드 상단에서 immer 불러오기

import produce from 'immer';

3) 첫번째 파라미터에는 수정하고 싶은 상태, 두번째 파라미터에는 어떻게 업데이트하고 싶을지 정의하는 함수를 넣어주기

const state = {
  number: 1,
  dontChangeMe: 2
};

const nextState = produce(state, draft => {
  draft.number += 1;
});

console.log(nextState);
// { number: 2, dontChangeMe: 2 }

Immer 와 함수형 업데이트

  • 리액트는 보통 useState로 함수형 업데이트를 한다.
  • Immer를 활용해 간편하게 useState를 사용할 수 있다.

  • 만약에 produce 함수에 첫번째 파라미터를 생략하고 바로 업데이트 함수를 넣어주게 된다면, 반환 값은 새로운 상태가 아닌 상태를 업데이트 해주는 함수가 된다.
const todo = {
  text: 'Hello',
  done: false
};

const updater = produce(draft => {
  draft.done = !draft.done;
});

const nextTodo = updater(todo);

console.log(nextTodo);
// { text: 'Hello', done: true }
  • produce가 반환하는 것이 업데이트 함수가 되기 때문에 useState의 업데이트 함수를 사용할 때 다음과 같이 구현할 수 있게 된다.
const [todo, setTodo] = useState({
  text: 'Hello',
  done: false
});

const onClick = useCallback(() => {
  setTodo(
    produce(draft => {
      draft.done = !draft.done;
    })
  );
}, []);

  • 성능적으로는 Immer를 사용하지 않은 코드가 조금 더 빠르다.
  • 하지만 데이터가 50,000개 가량 있는게 아니라면 성능 차이가 별로 없을 것이기 때문에 걱정하지 않아도 된다.
  • 데이터의 구조가 복잡해져 불변성을 유지하며 업데이트하는 코드가 복잡해진다면 Immer를 사용하는 것이 좋다.
  • 가능하면 데이터 구조가 복잡해지는 것을 방지하자.

참조:
23. Immer 를 사용한 더 쉬운 불변성 관리

post-custom-banner

0개의 댓글