1) 불변성이란?
`불변성이란 메모리에 있는 값을 변경할 수 없는 것`을 말합니다.
그래서 자바스크립트의 데이터 형태중에 원시 데이터 (숫자, 문자, 불리언) 들에게는 불변성이 있고, 원시 데이터가 아닌 객체, 배열, 함수는 불변성이 없습니다.
만약 우리가 let number = 1
이라고 선언을 하면, 메모리에는 1 이라는 값이 저장됩니다.
이때도 자바스크립트는 이미 메모리에 생성되어 있는 1이라는 값을 참조합니다.
즉, number와 secondNumber는 변수의 이름은 다르지만, 같은 메모리의 값을 바라보고 있는 것이죠. 그래서 우리가 콘솔에
number === secondNumber
를 하면true
가 보입니다.
import React from 'react'
function inde() {
let a = 1
let b = 1
console.log("1번째 질문 : a 와 b 의 값은 같을까요?",a === b)
// true
let obj1 = {name:"kim"}
let obj2 = {name:"kim"}
console.log("2번째 질문 : obj1 과 obj2 의 값은 같을까요?",obj1 == obj2)
//false
console.log(`3번째 질문 : ${obj1.name} 과 ${obj2.name}의 값은 같을까요?`,obj1.name == obj2.name)
// true
return (
<div>
불변성 체크
</div>
)
}
export default inde
예시로 든 코드의 결과물들을 보면 원시데이터인 let 변수에 할당한 값들은
true로 나오고 있지만, 객체의 값은 실제 값을 비교하지 않는이상, false가
나오는것을 확인 할 수 있습니다.
이는 자바스크립트에서 원시데이터가 아닌 객체나 배열 등은 참조하는 주소가 다르기때문에
실제로 값이 같을지라도 비교하는 과정에서는 참조하는 주소가 다르기 때문에
비교연산자로 확인했을때에는 다르다고 나오는 이유입니다.
리액트에서는 컴포넌트가 화면에 그려지기 위해선 렌더링을 해야하고
그 렌더링이 되기 위한 조건은 스테이트가 변경되는것을 말한다.
그래서 단순 변수는 무시한다
function App(){
const [inputTest, setInputTest] = useState("")
return(
<div>
<input
value={inputTest}
onChange={(event) =>
setInputTest(event.target.value)} />
<br />
내가 입력한 값 = {inputTest}
</div>
)
}
예제 코드를 실행하고 나온 화면에서 볼수 있듯이 setState 를 사용해서 실시간으로 변경이되기 때문에 새로고침같은 전체 페이지를 처음부터 다시 불러오는 큰 단위 렌더링이 아닌 효율적인 렌더링이 될 수 있다
하지만 여기서 주의해야될 부분이 나옵니다.
리액트에서는 화면을 리레더링 할지 말지 결정할 때 state의 변화를 확인합니다. state가 변했으면 리렌더링 하는 것이고, state가 변하지 않았으면 리렌더링을 하지 않죠.
그때, state가 변했는지 변하지 않았는지 확인하는 방법이 state의 변화 전, 후의 메모리 주소
를 비교합니다. 그래서 만약
리액트에서 원시데이터가 아닌 데이터를 수정할 때 불변성을 지켜주지 않고, 직접 수정을 가하면 값은 바뀌지만 메모리주소는 변함이 없게 되는것이죠.
그래서 즉, 개발자가 값은 바꿨지만 리액트는 state가 변했다고 인지하지 못하게 됩니다. 그래서 결국 마땅히 일어나야 할 리렌더링이 일어나지 않게되죠.
function App(){
let buttonClickCount = 0;
const [objTest, setObjTest] = useState({
name : "에스파",
members : 4
})
const onChangeHandler = () => {
objTest.name = "레드벨벳"
console.log(objTest)
setObjTest(objTest)
}
return(
<div>
<div>
그룹이름은 {objTest.name} 입니다.
</div>
<button
onClick={onChangeHandler}
>
이름변경버튼
</button>
</div>
)
}
export default App;
이것은 예시코드인데요, 이런식으로 작성했을때에 실제로 콘솔값을 확인해보면
실제로 objTest 의 내용은 변경이 되어있지만 리엑트는 스테이트 값이 변경되지
않았다고 인식되어 아무런 반응이 없는것을 확인할 수 있습니다.
그래서 코드를 변경해서 2가지 방법으로 이러한 문제를 해결할 수 있습니다.
function App(){
let buttonClickCount = 0;
const [objTest, setObjTest] = useState({
name : "에스파",
members : 4
})
const onChangeHandler = () => {
setObjTest({
name : "레드벨벳",
})
console.log(buttonClickCount)
}
return(
<div>
<div>
그룹이름은 {objTest.name} 입니다.
</div>
<button
onClick={onChangeHandler}
>
이름변경버튼
</button>
</div>
)
}
export default App;
function App(){
let buttonClickCount = 0;
const [objTest, setObjTest] = useState({
name : "에스파",
members : 4
})
const onChangeHandler = () => {
const objTestCopy = {...objTest}
objTestCopy.name = "레드벨벳"
setObjTest(objTestCopy)
}
return(
<div>
<div>
그룹이름은 {objTest.name} 입니다.
</div>
<button
onClick={onChangeHandler}
>
이름변경버튼
</button>
</div>
)
}
export default App;
배열을 setState 할 때 불변성을 지켜주기 위해, 직접 수정을 가하지 않고 전개 연산자를 사용해서 기존의 값을 복사하고, 그 이후에 값을 수정하는 식으로 구현합니다.
import React, { useState } from "react";
function App() {
const [dogs, setDogs] = useState(["말티즈"]);
function onClickHandler() {
// spread operator(전개 연산자)를 이용해서 dogs를 복사합니다.
// 그리고 나서 항목을 추가합니다.
setDogs([...dogs, "시고르자브르종"]);
}
console.log(dogs);
return (
<div>
<button onClick={onClickHandler}>버튼</button>
</div>
);
}
export default App;