Immer 사용법

Reducer에서 Immer 사용하기

import React, { useReducer, useMemo } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
import produce from 'immer';

function countActiveUsers(users) {
  console.log('활성 사용자 수를 세는중...');
  return users.filter(user => user.active).length;
}

const initialState = {
  users: [
    {
      id: 1,
      username: 'velopert',
      email: '[email protected]',
      active: true
    },
    {
      id: 2,
      username: 'tester',
      email: '[email protected]',
      active: false
    },
    {
      id: 3,
      username: 'liz',
      email: '[email protected]',
      active: false
    }
  ]
};

function reducer(state, action) {
  switch (action.type) {
    case 'CREATE_USER':
      return produce(state, draft => {
        draft.users.push(action.user);
      });
    case 'TOGGLE_USER':
      return produce(state, draft => {
        const user = draft.users.find(user => user.id === action.id);
        user.active = !user.active;
      });
    case 'REMOVE_USER':
      return produce(state, draft => {
        const index = draft.users.findIndex(user => user.id === action.id);
        draft.users.splice(index, 1);
      });
    default:
      return state;
  }
}

// UserDispatch 라는 이름으로 내보내줍니다.
export const UserDispatch = React.createContext(null);

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);

  const { users } = state;

  const count = useMemo(() => countActiveUsers(users), [users]);
  return (
    <UserDispatch.Provider value={dispatch}>
      <CreateUser />
      <UserList users={users} />
      <div>활성사용자 수 : {count}</div>
    </UserDispatch.Provider>
  );
}

export default App;

Immer와 함수형 업데이트

setTodo 함수 안에 업데이트 하는 함수를 넣음으로써 useCallback 을 사용하는 경우 두번째 파라미터인 deps 배열에 todo 를 넣지 않아도 되게 된다

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 }

이것을 useCallback에 적용해보면,

const [todo, setTodo] = useState({text: "hello", done: false});

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

Immer는 JS 엔진의 Proxy라는 기능을 사용 → react-native와 같은 환경에서는 지원되지 않으므로 ES5 fallback을 사용하게 됨