All'alba vincerò

At dawn, I will win!

React

[React] useImmer(): 상태(state)를 쉽게 관리

나디아 Nadia 2024. 8. 20. 14:11

📌 useImmer()

: React에서 상태 관리를 쉽게 할 수 있도록 도와주는 Hook

  • React의 useState 훅과 Immer 라이브러리가 제공하는 불변 업데이트 패턴을 결합한 커스텀 React 훅
  • React 상태 관리를 쉽게 하고, 복잡한 상태 업데이트를 간단하게 할 수 있는 훅
  • 중첩된 객체나 배열 상태를 업데이트할 때 유용
  • ❓언제 사용하면 좋은가
    • 중첩된 구조의 상태를 자주 업데이트하는 경우
    • 상태 업데이트 시 불변성을 유지해야 하는 경우
    • 코드의 가독성을 높이고 싶을 때

⏩ 장단점

  • 장점
    • 간단한 상태 관리
      : 중첩된 객체와 배열의 상태 업데이트를 간단하게 해준다.
    • 불변성 자동 관리
      : immer가 불변성을 자동으로 관리해 주므로, 직접 불변성을 신경 쓸 필요가 없다.
      - 상태가 의도치 않게 변형되는 것을 방지
    • 가독성 향상
      : 상태 업데이트 로직이 직관적이어서 코드 가독성이 향상된다.
  • 단점
    • 추가 의존성
      : `immer` 라이브러리에 의존하므로, 프로젝트에 추가적인 의존성을 더하게 된다.
    • 퍼포먼스 오버헤드
      : `immer`가 내부적으로 복잡한 상태 객체를 복제하고 불변성을 유지하는 작업을 하기 때문에
      경우에 따라 (미미한) 퍼포먼스 오버헤드가 발생할 수 있다. 

 
✨ Immer 라이브러리
: JavaScript의 불변성을 관리하는 라이브러리

  • 복잡한 상태 업데이트를 간단하고 직관적으로 할 수 있게 해준다.
  • immer는 상태 객체를 "초안(draft)" 형태로 제공하고, 이 초안을 직접 수정할 수 있다.
  • 👉 immer는 이 초안을 기반으로 불변성을 유지하는 새로운 상태 객체를 생성한다.


✅ 설치

pnpm add use-immer immer

 
 
 

✅ 사용

1. useImmer 가져오기

import { useImmer } from 'use-immer';

 
 
 
2. useImmer  훅 사용하기

const [state, updateState] = useImmer(initialState);

 

  • state: 현재 상태 값
  • updateState: 상태를 업데이트하는 데 사용되는 함수
  • initialState: useImmer 훅에 전달하는 초기 상태값
  • - 컴포넌트가 처음 렌더링될 때 사용될 상태의 초기값 정의

 
 
3. 상태 업데이트(draft)하기

updateState(draft => {
  // 여기서 draft 직접 수정
});
  • updateState: 함수를 인수로 받는다.
  • draft: 임시 사본
    • 가변성(Mutability)
      • draft는 일반적인 자바스크립트 객체처럼 직접 수정할 수 있다.
      • 하지만 실제로는 원본 상태를 변경하지 않는다.
    • 프록시(Proxy)
      • draft는 실제로 프록시 객체이다.
      • 이 프록시는 모든 변경사항을 추적한다.
    • 불변성 보장
      • draft를 수정하면, Immer가 이를 기반으로 새로운 불변 상태를 생성한다.
      • 원본 상태는 변경되지 않는다.

 

  • draft는 상태(state)의 임시 사본이다.
    • draft를 직접 수정하면, Immer가 이 수정사항을 기반으로 새로운 불변 상태를 만들어낸다.
const [user, updateUser] = useImmer({
  name: '홍길동',
  age: 30,
  hobbies: ['독서', '운동']
});

// 나이 증가
updateUser(draft => {
  draft.age += 1;
});

// 새 취미 추가
updateUser(draft => {
  draft.hobbies.push('음악 감상');
});

// 중첩된 객체 업데이트
updateUser(draft => {
  if (!draft.address) {
    draft.address = {};
  }
  draft.address.city = '서울';
});

 
 
 
예제 1. Todo 리스트

import React, { useState } from 'react';
import { useImmer } from 'use-immer';

function TodoList() {

  // 1. useImmer를 사용하여 todos 상태 관리
  const [todos, updateTodos] = useImmer([
    { id: 1, text: '리액트 공부하기', completed: false },
    { id: 2, text: '운동하기', completed: true },
  ]);

  const [newTodo, setNewTodo] = useState('');

  // 2. 할 일 추가 함수
  const addTodo = () => {
  
   // - draft.push()를 사용하여 새 항목을 직접 추가
    updateTodos(draft => {
      draft.push({ id: Date.now(), text: newTodo, completed: false });
    });
    
    setNewTodo('');
  };


  // 3. 할 일 완료 상태 토글 함수
  const toggleTodo = (id) => {
  
    // - draft에서 해당 항목을 찾아 직접 completed 속성을 변경
    updateTodos(draft => {
      const todo = draft.find(t => t.id === id);
      if (todo) {
        todo.completed = !todo.completed;
      }
    });
  };


  // 4. 할 일을= 삭제 함수
  const deleteTodo = (id) => {
  
    // - draft.splice()를 사용하여 항목을 직접 제거
    updateTodos(draft => {
      const index = draft.findIndex(t => t.id === id);
      if (index !== -1) {
        draft.splice(index, 1);
      }
    });
  };

  return (
    <div>
      <h1>할 일 목록</h1>
      <input
        type="text"
        value={newTodo}
        onChange={(e) => setNewTodo(e.target.value)}
        placeholder="새 할 일 입력"
      />
      <button onClick={addTodo}>추가</button>
      
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            <span 
              style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
              onClick={() => toggleTodo(todo.id)}
            >
              {todo.text}
            </span>
            <button onClick={() => deleteTodo(todo.id)}>삭제</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default TodoList;

 
 


 

GitHub - immerjs/use-immer: Use immer to drive state with a React hooks

Use immer to drive state with a React hooks. Contribute to immerjs/use-immer development by creating an account on GitHub.

github.com