state는 리액트에서 화면을 그려내는 데 굉장히 중요한 역할을 한다
State는 상태가 바뀔 때마다 화면을 새롭게 그려내는 방식으로 동작한다
import { useState } from 'react';
// ...
const [num, setNum] = useState(1);
// ...
보통 이렇게 Destructuring 문법으로 작성하는데 useState 함수가 초깃값을 아규먼트로 받고 그에 따른 실행 결과로 요소 2개를 가진 배열의 형태로 리턴을 하기 때문이다
import { useState } from 'react';
function App() {
const [num, setNum] = useState(1);
const handleRollClick = () => {
setNum(3); // num state를 3으로 변경!
};
const handleClearClick = () => {
setNum(1); // num state를 1로 변경!
};
자바스크립트의 자료형은 크게 기본형(Primitive type)과 참조형(Reference type)로 나눈다
const [gameHistory, setGameHistory] = useState([]);
const handleRollClick = () => {
const nextNum = random(6);
gameHistory.push(nextNum);
setGameHistory(gameHistory); // state가 제대로 변경되지 않는다!
};
위 코드에서 볼 수 있듯 배열 값을 가진 gameHistory에 push 메소드를 이용해서 배열의 값을 변경한 다음, 변경된 배열을 setter 함수로 state를 변경하려고 하면 코드가 제대로 동작하지 않는다.
그 이유는 gameHistory state는 배열 값 자체를 가지고 있는 게 아니라 그 배열의 주솟값을 참조하고 있는 것이기 때문에 push 메소드로 배열 안에 요소를 변경했다고 하더라도 결과적으로 참조하는 배열의 주솟값은 변경된 것이 아니게 된다.
결과적으로 리액트 입장에서는 gameHistory state가 참조하는 주솟값은 여전히 똑같기 때문에 상태(state)가 바뀌었다고 판단하지 않는 것이이다
그래서 참조형 state를 활용할 때는 반드시 새로운 참조형 값을 만들어 state를 변경해야 한다.
const [gameHistory, setGameHistory] = useState([]);
const handleRollClick = () => {
const nextNum = random(6);
setGameHistory([...gameHistory, nextNum]); // state가 제대로 변경된다!
};
Spread 문법(...) 을 활용하면 해결할 수 있다
리엑트가 랜더링 하는 방식
리엑트는 내부에서는 DOM트리를 본떠서 만든 virtual DOM이라는 자료구조를 사용한다
그래서 엘리먼트를 새로 랜더링할 떄 리엑트트 그 모습을 실제 DOM트리에 바로 반영하는것이 아니라 virtual DOM에 일단 적용한다
State 변경 전의 virtual DOM과 변경 후의 virtual DOM을 비교한다
바뀐 부분만 찾아낸 다음에 각각 해당하는 실제 DOM 노드를 변경한다
이런식으로 핸더링하면 좋은점
참고
코드잇