일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- node
- 자바문제풀이
- redux
- frontend
- github
- JavaScript
- cleancode
- HTML
- media query
- CSS
- TypeScript
- 그럼에도 불구하고
- 반응형 페이지
- JS
- java
- max-width
- 변수
- @media
- 그럼에도불구하고
- react-router-dom
- 프론트엔드
- 자바
- node.js
- 코드업
- webpack
- Servlet
- git
- react
- coding
- 코딩테스트
- Today
- Total
그럼에도 불구하고
[React] immer란? 본문
오늘은 immer에 대해 알아보겠습니다. :)
[ immer ]
immer는 React에서 구조가 복잡한 객체도 매우 쉽고 짧은 코드를 사용하여 불변성을 유지하면서 업데이트하기 위해 사용하는 라이브러리입니다.
📌 불변성이란?
쉽게 말해 상태를 변경하지 않는 것입니다.
[ install ]
yarn add immer
[ immer를 사용하지 않고 불변성 유지 ]
아래 예시는 immer를 사용했을 때와 비교하기 위해 작성된 코드입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | import React, { useRef, useCallback, useState } from 'react'; function 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({ ...form, [name] : [value] }); },[form]); // form 등록을 위한 함수 const onSubmit = useCallback( e => { e.preventDefault(); const info = { id: nextId.current, name: form.name, username: form.username }; // array에 새 항목 등록 setData({ ...data, array: data.array.concat(info) }); // form 초기화 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; | cs |
폼에서 아이디와 이름을 입력하면 하단 리스트에 추가되고, 리스트 항목을 클릭하면 삭제되는 컴포넌트입니다.
이렇게 전개 연산자와 배열 내장 함수를 사용하여 불변성을 유지하는 것은 어렵지 않지만, 상태가 복잡해진다면 조금 귀찮은 작업이 될 수 있습니다.
[ immer 사용법 ]
produce(수정하고 싶은 상태, 상태를 업데이트하는 함수)
produce라는 함수는 두 가지 파라미터를 받습니다. 첫 번째 파라미터는 수정하고 싶은 상태이고, 두 번째 파라미터는 상태를 어떻게 업데이트할지 정의하는 함수입니다.
두 번째 파라미터로 전달되는 함수 내부에서 원하는 값을 변경하면, produce 함수가 불변성 유지를 대신해 주면서 새로운 상태를 생성해 줍니다.
이 라이브러리의 핵심은 '불변성에 신경 쓰지 않는 것처럼 코드를 작성하되 불변성 관리는 제대로 해 주는 것'입니다. 단순히 깊은 곳에 위치하는 값을 바꾸는 것 외에 배열을 처리할 때도 매우 쉽고 편합니다.
[ App 컴포넌트에 immer 적용하기 ]
immer 라이브러리를 적용하지 않은 App 컴포넌트에 immer를 적용시켜보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | import React, { useRef, useCallback, useState } from 'react'; import {produce} from 'immer'; function 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; | cs |
만약, immer에서 제공하는 produce 함수를 호출할 때, 첫 번째 파라미터가 함수 형태라면 업데이트 함수를 반환합니다.
📌 예시
1 2 3 4 5 6 7 8 9 10 11 | const update = produce(draft => { draft.value = 2; }); const originalState = { value: 1, foo: 'bar', }; const nextState = update(originalState); console.log(nextState); // { value:2, foo: 'bar' } | cs |
이 방법을 토대로 App 컴포넌트를 수정해보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | import React, { useRef, useCallback, useState } from 'react'; import {produce} from 'immer'; function 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; | cs |
[ 정리 ]
오늘은 immer 라이브러리에 대해 알아보았습니다. 이 라이브러리는 컴포넌트의 상태 업데이트가 조금 까다로울 때 사용하면 매우 좋습니다. 이 라이브러리는 편의를 위한 것이므로 꼭 필요하지는 않지만, 사용한다면 생산성을 크게 높일 수 있습니다. 만약 immer를 사용하는 것이 오히려 불편하게 느껴진다면 사용하지 않아도 좋다고 생각합니다.
'React > React basics' 카테고리의 다른 글
[React] propTypes를 통한 props 검증 (0) | 2023.05.22 |
---|---|
[React] react-router-dom v6에 대해 알아보자 (0) | 2023.05.05 |
[React] props의 defaultProps란? (0) | 2023.04.28 |
[React] useMemo와 useCallback 이란? (0) | 2023.04.14 |
[React] Context API란? (0) | 2023.04.10 |