😎 immer를 설치하고 사용법 알아보기
- 전개 연산자와 배열의 내장 함수를 사용하면 간단하게 배역 혹은 객체를 복사하고 새로운 값을 덮어 쓸 수 있었다.
- 하지만 객체의 구조가 엄청나게 깊어지면 불변성을 유지하면서 이를 업데이트하는 것이 매우 힘들다.
- 예를 들어 아래와 같이 써야하는 것이다.
let nextObject = {
..object,
somewhere: {
...object.somewhere,
deep: {
...object.somewhere.deep,
array: object.somewhere.deep.array.concat(5)
}
}
}
- 기존 처럼 전개 연산자와 배열 내장 함수를 사용하여 불변성을 유지하는 예시는 아래와 같다
import React, { useRef, useCallback, useState } from "react";
import "./styles.css";
export default function App() {
const nextId = useRef(1);
const [form, setForm] = useState({ name: "", username: "" });
const [data, setData] = useState({
array: [],
uselessValue: null
});
const onChange = useCallback(
(e) => {
const { name, value } = e.target;
setForm({
...form,
[name]: [value]
});
},
[form]
);
const onSubmit = useCallback(
(e) => {
e.preventDefault();
const info = {
id: nextId.current,
name: form.name,
username: form.username
};
setData({
...data,
array: data.array.concat(info)
});
setForm({
name: "",
username: ""
});
nextId.current += 1;
},
[data, form.name, form.username]
);
const onRemove = useCallback(
(id) => {
setData({
...data,
array: data.array.filter((info) => info.id !== id)
});
},
[data]
);
return (
<div className="App">
<form onSubmit={onSubmit}>
<input
name="username"
placeholder="id"
value={form.username}
onChange={onChange}
/>
<input
name="name"
placeholder="name"
value={form.name}
onChange={onChange}
/>
<button type="submit">enroll</button>
</form>
<div>
<ul>
{data.array.map((info) => (
<li key={info.id} onClick={() => onRemove(info.id)}>
{info.username} ({info.name})
</li>
))}
</ul>
</div>
</div>
);
}
- 이를 immer를 사용하면 훨씬 간결하게 적을 수 있다.
- 그리고 immer의 사용 예시는 아래와 같다.
import produce from 'immer';
const originalState = [
{
id: 1,
todo: "yes",
checked: true,
},
{
id: 2,
todo: "no",
checked: false,
}
]
const nextState = produce(originalState, draft => {
const todo = draft.find(t => t.id === 2)
todo.checked = true
draft.push({
id: 3,
todo: "half",
checked: false,
})
draft.splice(draft.findIndex(t => t.id === 1), 1)
});
- immer를 이용하여 이전의 상태 관리 방법을 개선하면 그 코드는 아래와 같다.
export default function App() {
const onChange = useCallback(
(e) => {
const { name, value } = e.target;
setForm(
**produce(form, (draft) => {
draft[name] = value;
})**
);
},
[form]
);
const onSubmit = useCallback(
(e) => {
setData(
**produce(data, (draft) => {
draft.array.push(info);
})**
);
},
[data, form.name, form.username]
);
const onRemove = useCallback(
(id) => {
setData(
**produce(data, (draft) => {
draft.array.splice(
draft.array.findIndex((info) => info.id === id),
1
);
})**
);
},
[data]
);
}
😂 useState의 함수형 업데이트와 immer 함께 쓰기
- immer에서 제공하는 produce 함수를 호출할 때, 첫 번째 파라미터가 함수 형태라면 업데이트 함수를 반환한다.
- 예시 코드는 아래와 같다.
const update = produce(draft => {draft.value=2})
const originalState = {value:1, foo:'bar'}
const nextState = update(originalState)
- App.js에서 produce 맨 앞의 data 항을 지우면 적용된다.