immutable은 함수형 프로그래밍의 핵심원리중 하나이다. 객체가 생성된 이후 변할수 없다는 것을 뜻한다.
리액트의 state는 immutable하다고한다, 그런데 state 자체가 동적으로 변하는것 아닌가?? 불변성에 대해 알아보자
상태를 변경하지 않는것을 의미한다. 아래의 2가지 경우를 살펴보자
원시타입은 불변성을 가지고 있다.
let string = "hello"
string ="bye"
위의 경우 string은 bye로 바뀔것이다. 아 그럼 불변성이 깨진것 아닌가? 라고 생각할 수 있다.
하지만 메모리내에 hello, bye 둘다 남아있다. "bye"라는 새로운 값을 참조할 뿐이다. 참조 카운팅이 0이된 hello는 GC의 대상이 되어 메모리에서 사라지게 되겠다.
배열의 push 메소드는 원본 데이터를 수정한다. 이로서 불변성을 지켜주지 않은것이 되고, 새로운 배열 [1,2,3,4]를 할당해 새로운 참조값을 만들어 불변성을 지켜준것이 된다.
let array = [1, 2, 3, 4] // 메모리영역 1
array.push(5) // 메모리영역 1
array = [1, 2, 3, 4]
불변의 진짜 의미는 메모리영역에서 값을 변경할 수 없다는 의미이다.
리액트는 얕은 비교를 통해 상태업데이트를 한다. 얕은 비교란 객체의 프로퍼티를 비교하지않고 참고 주소값을 비교하여 변경을 확인한다.
얕은 비교는 계산리소스를 줄여주므로 효율적인 상태업데이트를 할 수 있다.
원시타입은 애초에 불변성이니 상관없으나, 참조타입인 객체,배열의 경우 원본데이터를 변경할 경우 원본데이터를 참조하는 다른 객체에서 예상치 못한 오류가 생길수 있다.
리액트는 얕은비교를 하기 때문에 만약 push 메소드를 이용해서 원본 배열을 바꾸게 되면 참조값은 그대로 이므로 리액트는 변경되지 않았다고 생각한다.
즉, state를 변경하고 dom을 변경하려면, 새로운 객체 or 배열을 만들어 새로운 참조값을 만들고 react에게 변경사항을 알려야 한다.
참조타입은 의도적으로 불변성을 지켜주어야한다
setUsers(state.array.concat(user))
const onRemove = id => {
// user.id가 id인것을 제거
setUsers(users.filter(user => user.id != id))
}
cosnt onToggle = id => {
setUsers(
users.map(user => user.id === id ? {...user, active: !user.active } : user))
}
setState(state => {...state, key:value})
setState(state => {...state, ket: newValue})
참조 블로그
https://kyounghwan01.github.io/blog/React/immer-js/#%E1%84%87%E1%85%AE%E1%86%AF%E1%84%87%E1%85%A7%E1%86%AB%E1%84%89%E1%85%A5%E1%86%BC-%E1%84%8C%E1%85%B5%E1%84%8F%E1%85%B5%E1%84%86%E1%85%A7%E1%86%AB%E1%84%89%E1%85%A5-state-%E1%84%87%E1%85%A1%E1%84%81%E1%85%AE%E1%84%80%E1%85%B5
https://kyounghwan01.github.io/blog/React/immer-js/#%E1%84%87%E1%85%AE%E1%86%AF%E1%84%87%E1%85%A7%E1%86%AB%E1%84%89%E1%85%A5%E1%86%BC-%E1%84%8C%E1%85%B5%E1%84%8F%E1%85%B5%E1%84%86%E1%85%A7%E1%86%AB%E1%84%89%E1%85%A5-state-%E1%84%87%E1%85%A1%E1%84%81%E1%85%AE%E1%84%80%E1%85%B5