Immutable & Mutable
immutable은 바뀔 수 없고 read-only 라는 뜻이다. number, string등의 원시 타입 값들이 이에 해당된다. 반면 object는 값 자체가 바뀔 수 있다. 이러한 객체는 mutable하다고 한다.
const position = { x: 0, y: 0 };
poisiton.x = 5; //mutable
React의 state는 immutable하다. object처럼 mutable한 객체이더라도, immutable하게 취급해야 한다.
어떤 object를 state로 두고 값을 바꾸고 싶다고 가정해보자. 아래와 같이 object를 mutable하게 취급하여 값 자체를 바꾸면 리액트가 의도한데로 동작하지 않는다.
const [position, setPosition] = useState({x: 0, y: 0});
onMove = (e) => {
position.x = 3;
position.y = 5;
}
object를 immutable하게 취급해야 제대로 동작한다. posiiton의 값을 직접 바꾸는 대신, 새로운 객체를 생성하여 setPosition에 넣어준다.
const [position, setPosition] = useState({x: 0, y: 0});
setPosition({
...position,
y: 5
});
그래서 react는 immutable하게 하기 위해서 모든 변하는 값마다 state를 만들어줘야한다. 당연히 코드도 길어진다. 왜 이렇게 복잡하게 할까?
Why Immutable
Debugging
console.log으로 객체를 찍어보았을 때, object의 값이 이미 변해버려서 이전과 이후를 비교할 수 없었던 경험이 있었을 것이다. 필자는 이럴 때 JSON.stringify로 값을 비교하곤 했다. 하지만 객체의 값도 immutable하게 취급한다면, 객체의 현재 상태, snapshot을 비교하기 훨씬 쉬워지고, 이것은 debugging에 큰 도움이 된다.
Optimizations
React의 최적화 전략에는 이전 props의 상태가 다음 props의 상태와 같다면, 그 부분을 뛰어넘는 방식이라고 한다. 이때 객체가 immutable하다면, 확인하는 작업이 더 빨라진다. 그냥 prevObj === obj 인지 확인하면 되기 때문이다.
New feature
새로운 리액트의 기능들은 object가 snapshot으로 취급된다는 가정하에 만들어졌다. 그렇게 사용하지 않으면 새로운 기능에 제한적일 것이다.
Requirement Changes
Undo, Redo, reset form등의 기능은 값이 immutable할 때 훨씬 편하다. 메모리에 이전 상태들을 가지고 있다가 적절한 때에 재사용이 가능하기 때문이다.
Simpler Implementation
오히려 단순해진다. 객체의 상태 변화를 감지하여 view가 다시 redering되는 것을 reactive하다고 한다. 이러한 reactive를 구현하기 위해 mutable한 다른 framework에서는 property hijacking이나, 모든 객체를 proxies로 감싸거나 등의 작업을 해야했다. 하지만, react는 immutable하기 때문에 state에 어떤 데이터이던, 얼마나 크던지 상관 없이, 퍼포먼서 저하 없이, 넣을 수 있다.
객체 복사하기
spread syntax
객체를 복사할 때는 spread syntax를 사용하면 편리하다. 다만 spread syntax는 shallow copy임에 주의하자(1level만 deep copy되어, 중첩인 경우 제대로 copy되지 않는다.) 중첩된 object인 경우 spread syntax를 여러번 써야 할 수 있다.
const [person, setPerson] = useState({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: '<https://i.imgur.com/Sd1AgUOm.jpg>',
}
});
setPerson({
...person,
artwork: {
...person.artwork,
city:'New Delhi'
}
})
Immer
객체가 너무 많이 중첩되어있는 경우에는 가장먼저, object를 flattening 하는 방법을 고려한다. 하지만 그러고 싶지 않을 때에는 Immer의 사용을 고려해보자.
Immer는 객체의 값을 바꾸면 값이 바뀐 새로운 객체를 return해주는 유명한 라이브러리다.
immer는 proxy로 감싸져 있어서 사용자가 바꾼 값들을 모두 기억해 두었다가, 아예 새로운 객체에 바뀐 값들을 적용하여 return하는 방식으로 작동한다.
npm i use-immer
import { useImmer } from 'use-immer'; // useState대신 사용
const [person, updatePerson] = useImmer({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: '<https://i.imgur.com/Sd1AgUOm.jpg>',
}
});
function handleNameChange(e) {
updatePerson(draft => {
draft.name = e.target.value;
});
}
'React' 카테고리의 다른 글
[Vite] Vite에서 절대경로(absolute path) Alias 사용하기 (0) | 2023.07.19 |
---|---|
[react] Dynamic Import (lazy) 의 Rule (0) | 2023.02.02 |
[React] React에서 절대경로 사용하기 (1) | 2022.09.23 |
react-redux (0) | 2021.07.26 |
React-bootstrap .css안먹을때 (0) | 2021.07.26 |