immer 사용해 불변성 유지하기

Haechan Kim·2022년 1월 19일
0

React

목록 보기
10/13


앞서 컴포넌트 업데이트 성능 최적화 방법, 불변성 유지하는것의 중요성 배움
전개 연산자와 배열 내장 함수 사용하면 배열/객체를 간단하게 복사/덮어쓰기 할 수 있다
하지만 객체 구도 깊어지면 불변성 유지하면서 업데이트 하기 매우 힘듦

const obj = {
    somewhere: {
        deep: {
            inside: 3,
            array: [1,2,3,4]
        },
        bar: 2
    },
    foo: 1
}

// somewhere.deep.inside 값 4로 바꾸기
let nextObj = {
    ...obj,
    somewhere: {
        ...obj.somewhere,
        deep: {
            ...obj.somewhere.deep,
            inside: 4
        }
    }
};

// somewhere.deep.array에 5 추가하기
let nextObj = {
    ...obj,
    somewhere: {
        ...obj.somewhere,
        deep: {
            ...obj.somewhere.deep,
            array: obj.somewhere.deep.array.concat(5)
        }
    }
}

복잡하고 가독성 떨어짐.
이런 상황에서 immer 라이브러리 사용하면 간단하다.

  • immer 사용법
import produce from 'immer';
const nextState = produce(originalState, draft => {
  // 바꾸고 싶은 값 바꾸기
  draft.somewhere.deep.inside = 5;
})

produce함수는 두 가지 파라미터 받음
첫번째는 수정하고 싶은 상태, 두번째는 상태 어떻게 업데이트할 지 정의하는 함수
두번째 함수 내부에서 원하는 값 변경하면 produce함수가 대신 불변성 유지해주면서 새로운 상태 생성

immer 라이브러리의 핵심은 불변성 신경 안쓰듯 코드 작성하되, 불변성 관리는 제대로 해 주는 것.

import React, {useRef, useCallback, useState} from 'react';
import produce from 'immer';

const App = () => {
  const nextId = useRef(1);
  const [form, setForm] = useState({name: 'username: '});
  const [data, setData] = useState({
    array: [],
    uselessValue: null
  });

  // input 수정 위한 함수
  const onChange = useCallback(e => {
    const {name, value} = e.target;
    setForm(
      produce(form, draft => {
        draft[name] = value;
      })
    );
  }, [form]);

  // form 등록 위한 함수
  const onSubmit = useCallback(e => {
    e.preventDefault();
    const info = {
      id: nextId.current,
      name: form.name,
      username: form.username
    };
    setData(
      produce(data, draft => {
        draft.array.push(info);
      })
    );

    setForm({
      name: '',
      username: ''
    });
    nextId.current += 1;
  }, [data, form.name, form.username]);

  // 항목 삭제 함수
  const onRemove = useCallback(id => {
    setData(
      produce(data, draft => {
        draft.array.splice(draft.array.findIndex(info => info.id === id), 1);
      })
    )
  }, [data]);

  return (
    <div>
      <form onSubmit={onSubmit}>
        <input
          name='username'
          placeholder='아이디'
          value={form.username}
          onChange={onChange}
        />
        <input
          name='name'
          placeholder='이름'
          value={form.name}
          onChange ={onChange}
        />
        <button type='submit'>등록</button>
      </form>
      <div>
        <ul>
          {data.array.map(info => (
            <li key={info.id} onClick={() => onRemove(info.id)}>
              {info.username} ({info.name})
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
};

export default App;

immer는 컴포넌트의 상태 업데이트가 까다로울 때 사용하면 좋다

0개의 댓글