라이브러리 없이 순수하게 상태값 업데이트 하기
const fruits = ["오렌지", "사과", "레몬", "바나나"];
라는 배열에서 “사과”, “레몬”을 제거하고 그 자리에 “딸기”를 넣어보자.
나쁜 예
fruits.splice(1, 2, "딸기");
console.log(fruits);
> [ '오렌지', '딸기', '바나나' ]
원하는대로 변경하긴 했지만 fruits 객체(props)를 변경하므로 불변성 유지 실패
좋은 예
const newFruits = [
...fruits.slice(0, 1),
"딸기",
...fruits.slice(3, 1)
];
새 배열 newFruits를 만들어서 원 배열은 유지한다.
fruits 배열을 풀어서(…) 0번 인덱스부터 1개 가져오고, 다음에 “딸기”를 넣고 다시 3번 인덱스부터 1개 가져온 뒤 배열([])로 묶어준다.
fruits와 newFruits를 보면 fruits는 원형 그대로, newFruits는 원하는대로 요소가 수정된 것을 확인할 수 있다.
console.log("fruits: ", fruits);
console.log("newFruits: ", newFruits);
> fruits: ['오렌지', '사과', '레몬', '바나나']
> newFruits: ['오렌지', '딸기', '바나나']
immer로 상태값 업데이트하기
우선 immer 라이브러리를 설치한다.
yarn add immer
import { produce } from "immer";
const newFruits = produce(fruits, draft => {
draft.splice(1, 2, "딸기");
});
product 함수에는 base 객체, receipt 함수가 인자로 들어간다.
draft는 원본 fruits 복사본이다. 복사본을 splice 하는 것이므로 fruits 객체는 불변성을 유지한다.
console.log("fruits: ", fruits);
console.log("newFruits: ", newFruits);
> fruits: ['오렌지', '사과', '레몬', '바나나']
> newFruits: ['오렌지', '딸기', '바나나']
좀 더 연습해보기
const baseState = [
{
todo: "Learn typescript",
done: true
},
{
todo: "Try immer",
done: false
}
];
- 두 번째 객체의 done을 true로 바꾸기
- 배열 마지막 요소로
{ todo: "Tweet about it" }
객체 추가하기
immer 사용하지 않기
const newState = [
...baseState.map(
(tweet, index) => {
return index !== 1 ? tweet : { ...tweet, done: true };
}
),
{
todo: "Tweet about it",
}
];
이건 컨닝했다 흑흑
baseState을 펼쳐서 map 함수로 각 객체와 그 인덱스 번호(키-값 쌍)를 저장한다.
1번 인덱스인 객체의 done 값을 true로 바꿔야 하므로, 인덱스가 1이 아닌 객체는 그대로 출력해주고 1일 때 done 키에 해당하는 값만 바꿔준다.
{ todo: “Tweet about it” } 는 새로 추가해야 하는 객체이므로 newState 배열 마지막 순서로 넣어준다.
비구조화와 삼항연산자를 잘 썼어야 하는 문제였다.
immer 사용하기
const newState = produce(baseState, draft => {
draft[1].done = true;
draft.push({ todo: "Tweet about it" });
})
두 케이스 모두 newState는 이렇게 나온다.
[
{ todo: 'Learn typescript', done: true },
{ todo: 'Try immer', done: true },
{ todo: 'Tweet about it' }
]