-
Notifications
You must be signed in to change notification settings - Fork 143
React state 불변성
eGovFrameSupport edited this page Apr 6, 2023
·
1 revision
- 데이터 변경이 일어나면(state, props)
- Virtual DOM에 렌더링을 한다.
- 업데이트 이전의 Virtual DOM과 현재의 Virtual DOM을 비교하여, 변경된 부분을 반영한다. (재조정)
- 변경 사항을 real DOM에 반영한다.
- React에서의 상태 변화 감지는 컴포넌트가 가지고 있는 데이터(state, props)의 비교로 이루어지는데 이 과정은 얕은 비교를 통해 이루어진다.
const [state, setState] = useState([1,2,3]);
const stateChange = () => {
state.push(4);
console.log(arr);
setState(arr);
}
};
- setState() 실행 시, 데이터가 변경되므로 일반적으로는 렌더링 작업이 재수행되지만, 데이터가 참조 타입(객체, 배열)이면 주의가 필요.
- 위 코드는 변경 전과 후의 데이터 내용은 다르지만 참조값이 동일하므로 동일한 것으로 판정, 렌더링이 되지 않는다.
- 불변성을 지켜야 한다는 원칙은 비단 react에서만 존재하는 새로운 컨셉이 아님.
- 불변성을 지키지 않을 경우, 데이터의 흐름을 추적하기 어렵고 이는 곧 예기치 못한 side effect나 버그로 이어진다.
- 또한 원본 데이터가 변경될 경우 이 원본 데이터를 참고하고 있는 다른 객체에서 예상치 못한 오류가 발생할 수 있으며 복잡도도 증가한다.
let x = {
name: 'gildong'
};
let y = x;
x.name = 'kim';
console.log(y.name); // kim
console.log(x === y) // true
let x = ['foo'];
let y = x;
x.push('bar');
console.log(y); // ['foo', 'bar']
console.log(x === y); // true
- 배열 및 객체와 같은 참조 타입의 데이터 변경시에는 원본을 직접 변경하지 않도록 한다.
- push(), splice(), sort() 와 같은 함수 또한 원본을 변경하기 때문에 사용하면 안 된다.
- 꼭 사용이 필요할 경우 기존의 데이터를 복사하고 그 복사본에 적용해 줘야 함
const user = { name: 'Choi', age: 25, friends: ['Park', 'Kim']};
const otherUser = { ...user, friends: [user.friends]};
user.name = 'Lee';
user.friends.push('Kang');
/*
user = { name: 'Lee', age: 25, friends: ['Park', 'Kim', 'Kang']};
otherUser = { name: 'Choi', age: 25, friends: ['Park', 'Kim']};
*/
user === otherUser // false
user.friends === otherUser.friends // false
- concat(), map(), filter(), slice(), spread operator를 이용.
- 단, spread operator는 얕은 복사를 실행하므로 다차원(배열 속 배열 or 객체 속 객체)의 비 원시 자료형값 복사를 위해서는 내부 값까지 전부 복사할 필요가 있음.
const compare = (a, b) => {
if(sortType === 'latest') {
return parseInt(b.date) - parseInt(a.date);
} else {
return parseInt(a.date) - parseInt(b.date);
}
}
const copyList = JSON.parse(JSON.stringify(diaryList));
const sortedList = copyList.sort(compare);
return sortedList;
- 객체를 한 번 JSON 형태의 문자열로 만들었다가 다시 객체화 시켜서 값만 새 변수에 할당
- sort()는 원본 배열을 변경하지만 이 경우는 별개의 복사본을 변경하므로 원본에는 영향을 주지 않음
const objects = [{ 'a' : 1 },{ 'b' : 2 }];
const deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
- lodash 라이브러리의 cloneDeep()을 이용
- 외부 라이브러리이므로 추가 인스톨 필요
const state = {
number: 1,
dontChangeMe: 2
};
const nextState = produce(state, draft => {
draft.number += 1;
});
console.log(nextState);
// {number: 2, dontChangeMe: 2};
- immer 라이브러리의 produce()를 이용
- 첫 파라미터는 수정하고 싶은 state, 두 번째는 업데이트 정의 함수(불변성 고려 필요 없음)
- 외부 라이브러리이므로 추가 인스톨 필요