상세 컨텐츠

본문 제목

[리액트를 다루는 기술] 12장 immer를 사용하여 더 쉽게 불변성 유지하기

21-22/21-22 리액트 마스터

by Kimpeep 2021. 12. 27. 13:00

본문

728x90

객체의 구조가 엄청나게 깊어지면 불변성을 유지하면서 이를 업데이트하기가 매우 힘듦

전개연산자를 여러 번 사용하는 것보다 immer라는 라이브러리 사용하는 것이 더 쉽게 불변성 유지 가능

12.1 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;


전개 연산자와 배열 내장 함수를 사용하여 불변성 유지하는 것이 어렵지는 않지만,

  ⇒ 상태가 복잡해질 경우, 귀찮은 작업이 될 수 있음

 

 

immer 사용법

import produce from 'immer';
const nextState = produce(originalState, draft => {
	draft.somewhere.deep.inside = 5;
})

⇒ 불변성에 신경쓰지 않는 것처럼 코드를 작성하되 불변성 관리는 제대로!

produce 함수

  • 첫번째 파라미터 : 수정하고 싶은 상태
  • 두번째 파라미터 : 상태를 어떻게 업데이트할지 정의하는 함수

12.1.4 App 컴포넌트에 immer 적용하기

▼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를 사용할 때는, 배열에 직접적인 변화를 일으키는 push, splice 등의 함수를 사용해도 무방
  • immer는 불변성을 유지하는 코드가 복잡할 때만 사용해도 충분

12.1.5 useState의 함수형 업데이트와 immer 함께 쓰기

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;

Quiz

Q1. produce 함수의 첫 번째 파라미터는 _____를 나타내고, 두번째 파라미터는 상태를 어떻게 업데이트 할지 정의하는 함수를 나타낸다.

더보기

답 : 수정하고 싶은 상태

Q2. immer를 사용할 때는, 배열에 직접적인 변화를 일으키는 push, splice 등의 함수를 사용해서는 안 된다. (O,X)

더보기

답 : X

Q3. immer에서 제공하는 produce 함수를 호출할 때, 업데이트 함수를 반환하고 싶다면 첫번째 파라미터에 _____를 넣는다.

더보기

답 : 함수 형태

728x90

관련글 더보기