객체의 구조가 엄청나게 깊어지면 불변성을 유지하면서 이를 업데이트하기가 매우 힘듦
전개연산자를 여러 번 사용하는 것보다 immer라는 라이브러리 사용하는 것이 더 쉽게 불변성 유지 가능
yarn create react-app immer-tutorial
cd immer-tutorial
yarn add immer
▼immer를 사용하지 않고 불변성 유지 하는 코드
//App.js
import React, {useRef, useCallback, useState } from 'react';
const 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>
<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;
전개 연산자와 배열 내장 함수를 사용하여 불변성 유지하는 것이 어렵지는 않지만,
⇒ 상태가 복잡해질 경우, 귀찮은 작업이 될 수 있음
import produce from 'immer';
const nextState = produce(originalState, draft => {
draft.somewhere.deep.inside = 5;
})
⇒ 불변성에 신경쓰지 않는 것처럼 코드를 작성하되 불변성 관리는 제대로!
produce 함수
▼App 컴포넌트에 immer 적용한 코드
//App.js
import React, {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
});
const onChange = useCallback(
e=>{
const {name, value} = e.target;
setForm({
produce(form, draft => {
draft[name] = value;
})
);
},
[form]
);
const onSubmit = useCallback(
e => {
e.preventDefault();
const info = {
id: nextId.current,
name: form.name,
username: form.username
};
setData({
produce(data, draft => {
draft.array.push(info);
})
);
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에서 제공하는 produce 함수를 호출할 때, 첫번째 파라미터가 함수 형태라면 업데이트 함수 반환함
▼App 컴포넌트에 함수형 업데이트+immer 사용한 코드
//App.js
import React, {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
});
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
};
setData({
produce(draft => {
draft.array.push(info);
})
);
setForm({
name:'',
username:''
});
nextId.current += 1;
},
[data, form.name, form.username]
);
const onRemove = useCallback(
id => {
setData({
produce(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;
Q1. produce 함수의 첫 번째 파라미터는 _____를 나타내고, 두번째 파라미터는 상태를 어떻게 업데이트 할지 정의하는 함수를 나타낸다.
답 : 수정하고 싶은 상태
Q2. immer를 사용할 때는, 배열에 직접적인 변화를 일으키는 push, splice 등의 함수를 사용해서는 안 된다. (O,X)
답 : X
Q3. immer에서 제공하는 produce 함수를 호출할 때, 업데이트 함수를 반환하고 싶다면 첫번째 파라미터에 _____를 넣는다.
답 : 함수 형태
[리액트를 다루는 기술] 14장 외부 API를 연동하여 뉴스 뷰어 만들기 (0) | 2022.01.03 |
---|---|
[리액트를 다루는 기술]13장 리액트 라우터로 SPA 개발하기 (0) | 2021.12.27 |
[리액트를 다루는 기술] 11장 컴포넌트 성능 최적화 (0) | 2021.12.27 |
React와 Express를 사용해 메모장 만들기(Hooks 사용 ver.) (0) | 2021.12.01 |
MEMO 프로젝트 (함수형) - DB 연결 없는 Ver. (0) | 2021.12.01 |