불변성, 불변성! 리덕스를 사용하면서 귀에 딱지 앉게 들었을 불변성 🙉 상태는 절대 절대 직접 변경하면 안된다. 특히나 리덕스는 객체의 상태 변화를 얕은 수준에서만 인식할 수 있기 때문에 객체의 깊은 상태를 변경하면 리덕스가 상태 변화를 못 알아차릴 수도 있다.
예를 들어 아래와 같이 깊은 객체가 있다고 한다면, 우리는 이 객체를 변경하기 위해
state = {
병원: {
소아병동: {
207호: {
환자1: {
성별: 남,
퇴원예정일: 3일 후 // 이 정보를 오늘로 바꾸어 보자!
},
...
},
...
}
}
}
아래처럼 새로운 객체를 생성하여 깊고 깊게 파고 들어가 변경을 해야한다.
const { 병원 } = this.state;
this.setState({
병원: {
...병원,
소아병동: {
...병원.소아병동,
207호: {
...병원.소아병동.207호,
환자1: {
...병원.소아병동.207호.환자1,
퇴원예정일: 오늘🌈
}
}
}
}
})
번거롭당. 아주. 이런 번거로움을 해결해주는 library 가 있다.
immer
immer 를 통해 상태 불변성을 유지하며 손쉽게 상태 변경이 가능하다.
$ yarn add immer
// 기존
(...)
export default function counter(state=initialState, action) {
switch(action.type) {
case CHANGE_COLOR:
return {
...state,
color: action.color,
}
case INCREMENT:
return {
...state,
number: state.number + 1,
}
case DECREMENT:
return {
...state,
number: state.number - 1,
};
default:
return state;
}
}
// immer 사용
import produce from 'immer';
(...)
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;
}
}
얕은 객체에 있어서는 immer의 편리성을 크게 실감할 수 없지만, 깊은 객체일수록 immer의 편리성과 고마움은 커진다.
// 기존
export default handleActions(
{
[CHANGE_INPUT] : (state, action) => ({
...state,
input:action.payload
}),
[CREATE] : (state,action) => ({
...state,
list: state.list.concat({
id: action.payload.id,
name: action.payload.text,
entered: false,
})
}),
[ENTER] : (state, action) => ({
...state,
list: state.list.map((user) => user.id === action.payload ? {...user, entered: !user.entered }: user )
}),
[LEAVE] : (state, action) => ({
...state,
list: state.list.filter((user) => user.id !== action.payload)
})
}, initialState);
// immer 을 사용한 reducer 생성
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);
공부 자료 ✍️ / velog.io/@velopert/Redux-3-리덕스를-리액트와-함께-사용하기-nvjltahf5e
'FE > React' 카테고리의 다른 글
React HOC에 대해 알아보자 (0) | 2022.02.07 |
---|---|
useSelector, useDispatch로 리덕스에 편하게 접근해보자 (1) | 2022.01.23 |
React 렌더링 퍼포먼스 개선 (3) | 2021.12.29 |
Redux 기초 개념 요약본 / 리덕스란? (0) | 2021.02.15 |
리덕스가 등장하기 전까지 이야기, MVC 패턴의 한계와 Flux의 등장 (0) | 2021.02.15 |