[React] 불변성 유지하기

vanLan·2022년 12월 2일
0

React

목록 보기
10/11
post-thumbnail


🥇 불변성

  • 기존의 값을 직접 수정하지 않으면서 새로운 값을 만들어 내는 것.
  • 불변성이 지켜지지 않으면 객체 내부의 값이 새롭게 바뀌어도 React에서 감지하지 못한다. 그러면 React.memo에서 서로 비교하여 최적화하는 것이 불가능하다.


🥈 불변성 유지하기

  • 전개 연산자와 배열의 내장 함수를 사용하면 간단하게 배열 혹은 객체를 복사하고 새로운 값을 덮어 쓸 수 있다.
    하지만, 객체의 구조가 엄청 깊어지면 불변성을 유지하면서 이를 업데이트 하는 것이 매우 힘들다.
    
    const object = {
      somewhere: {
        deep: {
          inside: 3,
          array: [1, 2, 3, 4]
        },
        bar: 2
      },
      foo: 1
    };
    
    // somewhere.deep.array에 5 추가
    let nextObject = {
      ...object,
      somewhere: {
        ...object.somewhere,
        depp: {
          ...object.somewhere.deep,
          array: object.somewhere.deep.array.concat(5)
        }
      }
    };
    • 위와 같이 깊은 상태를 다룰 때 전개 연산자를 여러번 사용하는 것은 꽤 번거로운 작업이다.
      이러한 상황에 immer 라이브러리를 사용하면, 구조가 복잡한 객체도 매우 쉽고 짧은 코드를 사용하여 불변성을 유지하면서 업데이트 할 수 있다.

🥈 immer

  • 불변성을 신경 쓰지 않는 것처럼 코드를 작성하지만 불변성 관리를 제대로 해준다.

  • 단순히 깊은 곳에 위치하는 값을 변경하는 것 외 배열을 처리할 때도 매우 유용.

    🥉 설치 방법

    • yarn add immer

    🥉 사용법

    
    import produce from 'immer';
      
    const nextState = produce(originalState, draft => {
      // 바꾸고 싶은 값 바꾸기
      draft.somewhere.deep.inside = 5;
    });
    • produce(수정하고 싶은 상태, 상태 변경을 정의하는 함수)
      두번째 파라미터로 전달되는 함수 내부에서 원하는 값을 변경하면, produce 함수가 대신하여 불변성을 유지 시킨 새로운 상태를 생성.

    🥉 예시

    
    import produce from "immer";
    import { useCallback, useState, useRef } from "react";
    
    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,
          };
    
          // array에 새 항목 등록
          setData(
            produce(data, (draft) => {
              draft.array.push(info);
            })
          );
    
          // form 초기화
          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를 사용하여 컴포넌트 상태를 작성할 때 객체 안에 있는 값을 직접 수정하거나, 배열에 직접적인 변화를 일으키는 push, splice 등의 함수를 사용해도 된다.
      (불변성 유지에 익숙하지 않아도 자바스크립트에 익숙하다면 컴포넌트 상태에 원하는 변화를 쉽게 반영 시킬 수 있다.)


📀 정리

  • immer 라이브러리는 컴포넌트 상태 업데이트가 조금 까다로울 때 사용하면 매우 좋다.
  • 상태 관리 라이브러리인 Redux를 사용할 때도 immer를 쓰면 코드를 매우 쉽게 작성할 수 있다.
  • 라이브러리는 편의를 위한 것 이므로 꼭 필요하진 않지만, 사용하면 생산성을 크게 높일 수도 있으나 immer 사용이 불편하게 느껴진다면 사용하지 않아도 좋다.


profile
프론트엔드 개발자를 꿈꾸는 이

0개의 댓글