무지성스럽게 불변성 관리를 할 수 있다길래 배웠다.
솔직히 지금까지 한 프로젝트에서 immer라 필요한 정도로 depth가 깊게 들어간 객체를 불변성 유지할 일이 없어서 사용을 안해왔다.
그 정도로 복잡해지기 전에 어떻게든 객체를 쪼개놓아야 한다는 생각이었다.
그런데 아무래도 다음 프로젝트에서는 사용해야 할 것 같아서 찾아보기로. ㅎ
아래는 velopert가 reducer에 immer를 붙인 꼴이다. 아래와 같은 immer의 produce 함수를 활용하면 된다.
produce(currentState, producer: (draftState) => void): nextState
일반 local state에 사용해도 되고, redux에서 사용해도 된다.
local state에서 사용할 사람들은 아래 글을 읽어보자.
- react에서는 알아보니 use-immer라는 녀석이 표준처럼 쓰이고 있더라. immerjs 공식 패키지니 이용 안 할 이유가 없을 듯.
https://github.com/immerjs/use-immer
- custom 훅으로 만들어서 사용하는 예는 아래 참고.
https://css-tricks.com/using-immer-for-react-state-management/
사용법은 동일하니 redux의 reducer에서 사용하는 것으로 코드를 살펴보자.
이 코드는 기존, 불변성 관리를 위한 툴 없이 얕은 복사로 처리하던 코드다.
const postReducer = (prevState = initialState, action) => {
switch (action.type) {
case ADD_POST:
return [...prevState, action.data];
default:
return prevState;
}
};
이 코드를 아래와 같이 immer의 produce를 활용하여, 일반 객체를 수정하듯 관리할 수 있다.
const postReducer = (prevState = initialState, action) => {
switch (action.type) {
case ADD_POST:
return produce(prevState, (draft) => {
draft.push(action.data);
});
default:
return prevState;
}
};
이런식으로 작성해도 되지만, 아래와 같이 case가 많아질 경우 produce를 여러번 써주는 것이 귀찮을 수도 있다.
그러면 아래처럼 작성하는 경우도 있는데, 개인적으로는 위의 방법이 더 직관적인 것 같다.
이상하게 아래 같은 꼴로 사용하면 store에서 initialValue가 undefined이라는 둥 (분명 초기값을 넣어줬음에도) 사용하면서 불평이 많다.
const postReducer = (prevState = initialState, action) => {
produce(prevState, (draft) => {
switch (action.type) {
case ADD_POST:
draft.push(action.data);
default:
break;
}
});
};
* trouble shootings
1. Uncaught Error: [Immer] An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.
새 값을 반환하던가, draft를 수정하던가 둘 중 하나만 하라는 거라는 말이다.
에러 문구에 나와있지는 않지만, 쉽게 말하자면 정의 상에는 void를 반환하도록 되어 있으니까 그걸 좀 따라주자.
이게 다 타입스크립트를 안 써서 발생한 문제다.
// Wrong
return produce(prevState, (draft) => draft.push(action.data));
// Good
return produce(prevState, (draft) => {
draft.push(action.data);
});
2. commonjs랑 ESModule 방식이 좀 다르더라. 패키지 정의 부분 살펴보면 방식에 대한 차이를 이해할 수 있다.
const { produce } = require("immer");
import produce from "immer"
그 외 예시들)
1. velopert의 모던 리액트에서 나와있던 코드.
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;
}
}
ref)
'State Management > ⚛ Redux' 카테고리의 다른 글
redux toolkit with typescript (0) | 2021.08.07 |
---|---|
redux-toolkit (1) : redux와 RTK를 비교해보자 (0) | 2021.06.27 |
redux-saga를 통한 redux 비동기 처리 (1) : redux-saga 설치 및 구성 (0) | 2020.08.14 |
Redux 미들웨어, redux-thunk (0) | 2020.07.18 |
redux-action으로 액션, 리듀서를 간략히 작성하기 (0) | 2020.07.18 |