9. Immer 로 불변성 유지하기
Immer 는 굉장히 편리한 불변성 유지 라이브러리입니다.
먼저 설치를 해주세요.
$ yarn add immer
Immer 사용법
Immer 사용법은, 너무 쉽습니다. 마치 불변성에 대해서 신경쓰지 않는 것 처럼 데이터를 업데이트 해주면, 라이브러리가 알아서 불변성 유지를 해주면서 업데이트를 해줍니다.
import produce from 'immer';
const baseState = [
{
todo: 'Learn typescript',
done: true,
},
{
todo: 'Try immer',
done: false,
},
];
const nextState = produce(baseState, draftState => {
draftState.push({ todo: 'Tweet about it' });
draftState[1].done = true;
});
counter.js 모듈에 Immer 적용
src/store/modules/counter.js
import produce from 'immer'; // **** immer 불러오기
// 액션 타입 정의
const CHANGE_COLOR = 'counter/CHANGE_COLOR';
const INCREMENT = 'counter/INCREMENT';
const DECREMENT = 'counter/DECREMENT';
// 액션 생섬함수 정의
export const changeColor = color => ({ type: CHANGE_COLOR, color });
export const increment = () => ({ type: INCREMENT });
export const decrement = () => ({ type: DECREMENT });
// 초기상태 정의
const initialState = {
color: 'red',
number: 0,
};
// 리듀서 작성
// **** 내부 업데이트 로직 모두 수정
export default function counter(state = initialState, action) {
switch (action.type) {
case CHANGE_COLOR:
return produce(state, draft => {
draft.color = action.color;
});
case INCREMENT:
return produce(state, draft => {
draft.number++;
});
case DECREMENT:
return produce(state, draft => {
draft.number--;
});
default:
return state;
}
}
어떤가요? 너무 간단하죠? 신기하게도 잘 됩니다..
waiting.js 모듈에 Immer 적용
이렇게 배열이 있는 곳에서 사용하면, 편리함을 실감하실 수 있습니다.
src/modules/waiting.js
import { createAction, handleActions } from 'redux-actions';
import produce from 'immer'; // **** Immer 불러오기
const CHANGE_INPUT = 'waiting/CHANGE_INPUT'; // 인풋 값 변경
const CREATE = 'waiting/CREATE'; // 명단에 이름 추가
const ENTER = 'waiting/ENTER'; // 입장
const LEAVE = 'waiting/LEAVE'; // 나감
let id = 3;
// createAction 으로 액션 생성함수 정의
export const changeInput = createAction(CHANGE_INPUT, text => text);
export const create = createAction(CREATE, text => ({ text, id: id++ }));
export const enter = createAction(ENTER, id => id);
export const leave = createAction(LEAVE, id => id);
// **** 초기 상태 정의
const initialState = {
input: '',
list: [
{
id: 0,
name: '홍길동',
entered: true,
},
{
id: 1,
name: '콩쥐',
entered: false,
},
{
id: 2,
name: '팥쥐',
entered: false,
},
],
};
// handleActions 로 리듀서 함수 작성
// **** 내부 업데이트 로직 모두 업데이트
export default handleActions(
{
[CHANGE_INPUT]: (state, action) =>
produce(state, draft => {
draft.input = action.payload;
}),
[CREATE]: (state, action) =>
produce(state, draft => {
draft.list.push({
id: action.payload.id,
name: action.payload.text,
entered: false,
});
}),
[ENTER]: (state, action) =>
produce(state, draft => {
const item = draft.list.find(item => item.id === action.payload);
item.entered = !item.entered;
}),
[LEAVE]: (state, action) =>
produce(state, draft => {
draft.list.splice(
draft.list.findIndex(item => item.id === action.payload),
1
);
}),
},
initialState
);
정말 간단하죠? 이러한 편리성 때문에, 정말 강력 추천드리고 싶고, 저의 경우엔 새로 작성하는 리덕스 모듈에서는 Immer 라이브러리를 애용하고있습니다.