앞선 단원에서 전개 연산자와 배열의 내장 함수를 사용하여 배열 혹은 객체를 복사하고 새로운 값을 덮어 쓰는 것을 배웠습니다. 하지만 객체의 구조가 깊어진다면 불변성을 유지하면서 업데이트하는 것은 힘든 일입니다.
이러한 상황에 immer라이브러리를 사용해볼 수 있습니다. 이번 단원에서는 다음과 같은 순서로 진행합니다.
$ yarn add immer
immer라이브러리를 사용하면 불변성을 유지하면서 간단하게 값을 업데이트할 수 있습니다.
import produce from 'immer';
const nextState = produce(originalState, draft => {
// 바꾸고 싶은 값 바꾸기
draft.somewhere.deep.inside = 5;
produce 함수의 첫 번째 파라미터는 수정하고 싶은 상태이고, 두 번째 파라미터는 상태를 어떻게 업데이트할지 정의하는 함수입니다.두번째 파라미터로 전달되는 함수 내부에서 원하는 값을 변경하면, produce 함수가 불변성 유지를 대신해 주면서 새로운 상태를 생성합니다.
immer 라이브러리의 핵심은 '불변성에 신경 쓰지 않는 것처럼 코드를 작성하되 불변성 관리는 제대로해 주는 것'입니다. 깊은 곳에 위치하는 값을 바꾸는 것뿐만 아니라 배열을 처리할 때도 쉽게 처리할 수 있습니다.
import produce from 'immer';
const originalState = [
{
id: 1,
todo: '전개 연산자와 배열 내장 함수로 불변성 유지하기',
checked: true,
},
{
id: 2,
todo: 'immer로 불변성 유지하기',
checked: false,
}
];
const nextState = produce(originalState, draft => {
// id가 2인 항목의 checked 값을 true로 설정
const todo = draft.find(t => t.id === 2); // id로 항목 찾기
todo.checked = true;
// 혹은 draft[1].checked = true;
// 배열에 새로운 데이터 추가
draft.push({
id: 3,
todo: '일정 관리 앱에 immer 적용하기',
checked false,
});
// id = 1인 항목 제거하기
draft.splice(draft.findIndex(t => t.id === 1), 1);
});
// App.js
import { useRef, useCallback, useState } from 'react';
import produce from 'immer';
const App = () => {
const nextId = useRef(1);
const [form, setForm] = useState({ name: '', username: '' });
const [data, setData] = useState({
array: [],
uselessValue: null
});
// input 수정을 위한 함수
const onChange = useCallback(e => {
const { name, value } = e.target;
setForm(
produce(form, draft => {
draft[name] = value;
})
);
}, [form]);
// form 등록을 위한 함수
const onSubmit = useCallback(
e => {
e.preventDefault();
const info = {
id: nextId.current,
name: form.name,
username: form.username
};
// array 에 새 항목 등록
setData(
produce(data, draft => {
draft.array.push(info);
})
);
// form 초기화
setForm({
name: '',
username: ''
});
nextId.current += 1;
},
[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]
);
return (
<div>
<form onSubmit={onSubmit}>
<input
name="username"
placeholder="아이디"
value={form.username}
onChange={onChange}
/>
<input
name="name"
placeholder="이름"
value={form.name}
onChange={onChange}
/>
<button type="submit">등록</button>
</form>
<div>
<ul>
{data.array.map(info => (
<li key={info.id} onClick={() => onRemove(info.id)}>
{info.username} ({info.name})
</li>
))}
</ul>
</div>
</div>
);
};
export default App;
immer를 사용하여 컴포넌트 상태를 작성할 때는 객체 안에 있는 값을 직접 수정하거나, 배열에 직접적인 변화를 일으키는 push, splice 등의 함수를 사용해도 무방합니다. 물론 immer를 사용한다고 해서 무조건 코드가 간결해지는 것은 아닙니다. 예를 들어 onRemove의 경우에는 배열 내장 함수 filter를 사용하는 것이 코드가 더 깔끔하므로, 굳이 immer를 적용할 필요가 없습니다.
// App.js
import { useRef, useCallback, useState } from 'react';
import produce from 'immer';
const App = () => {
const nextId = useRef(1);
const [form, setForm] = useState({ name: '', username: '' });
const [data, setData] = useState({
array: [],
uselessValue: null
});
// input 수정을 위한 함수
const onChange = useCallback(e => {
const { name, value } = e.target;
setForm(
produce(draft => {
draft[name] = value;
})
);
}, []);
// form 등록을 위한 함수
const onSubmit = useCallback(
e => {
e.preventDefault();
const info = {
id: nextId.current,
name: form.name,
username: form.username
};
// array 에 새 항목 등록
setData(
produce(draft => {
draft.array.push(info);
})
);
// form 초기화
setForm({
name: '',
username: ''
});
nextId.current += 1;
},
[form.name, form.username]
);
// 항목을 삭제하는 함수
const onRemove = useCallback(
id => {
setData(
produce(draft => {
draft.array.splice(draft.array.findIndex(info => info.id === id), 1);
})
);
},
[]
);
return (
<div>
<form onSubmit={onSubmit}>
<input
name="username"
placeholder="아이디"
value={form.username}
onChange={onChange}
/>
<input
name="name"
placeholder="이름"
value={form.name}
onChange={onChange}
/>
<button type="submit">등록</button>
</form>
<div>
<ul>
{data.array.map(info => (
<li key={info.id} onClick={() => onRemove(info.id)}>
{info.username} ({info.name})
</li>
))}
</ul>
</div>
</div>
);
};
export default App;
문제
1.기존에는 (), ()를 사용하여 배열, 객체의 데이터들을 원하는 값으로 새로 지정할 수 있었다.
2. immer 라이브러리를 사용하면 ()하며 값을 업데이트를 간단하게 할 수 있다.
3. produce 함수에서 첫 번째 파라미터는 ()상태이다.
4. produce 함수에서 두 번째 파라미터는 () 함수이다.
5. immer를 사용할 때 배열에 직접적인 변화를 일으키는 (), () 등의 함수를 사용해도 무방하다.
6. immer를 사용한다고 코드가 항상()해지는 것은 아니며, 불변성 유지하는 코드가 ()때만 사용하면 된다.
7. immer 속성과 ()를 함께 사용하면 코드를 더욱 깔끔하게 만들 수 있다.
8. immer 라이브러리를 사용하기위해 필요한 import 코드를 작성하여라.
9. id가 1인 항목의 checked 값을 true로 설정하는 함수를 작성하여라.
import produce from 'immer';
const originalState = [
{
id: 1,
todo: '전개 연산자와 배열 내장 함수로 불변성 유지하기',
checked: true,
},
{
id: 2,
todo: 'immer로 불변성 유지하기',
checked: false,
}
];
//코드 작성
정답
import produce from 'immer';
const nextState = produce(originalState, draft => {
const todo = draft.find(t => t.id === 1);
todo.checked = true;
});
[리액트스타터2] 16장. 리덕스 라이브러리 이해하기 (0) | 2023.01.12 |
---|---|
[리액트스타터2] 13장. 리액트 라우터로 SPA 개발하기 (0) | 2023.01.05 |
[리액트스타터2] 11장. 컴포넌트 성능 최적화 (0) | 2022.12.29 |
[리액트스타터2] 10장. 일정 관리 웹 애플리케이션 만들기 (0) | 2022.12.22 |
[리액트스타터2] 9장. 컴포넌트 스타일링 (0) | 2022.12.01 |