본문 바로가기

React

React - state, react가 render하는 방식

  • react의 변수같은 것.
  • state를 바꾸면 react가 알아서 화면을 새로 렌더링 해준다.
  • 화면을 조건에 따라 변경해야할 때 활용한다.
  • state를 변경할 때 마다 화면을 새로그린다
// App.js
import { useState } from 'react';
import Dice from "./Dice";
import Button from "./Buttom";

function App(){
    const [num, setNum] = useState(1);
    const handleRollClick = () => {
        setNum(3);
    }
    return (
        <div>
            <div>
                <Button onClick={handleRollClick}>던지기</Button>
                <Button>처음부터</Button>
            </div>
            <Dice color="red" num={num}/>
        </div>
    );
}
export default App;
  • import를 해준 후 사용할 수있다.
  • state는 const를 사용해 만든다.
  • 위 코드에서 useState함수는 파라미터로 초기값을 받고, 배열을 리턴한다는 것을 알 수 있다.
  • 따라서 destructuring문법으로 위와같이 작성한다.
  • state의 값을 변경하고자 할 때에는 직접 변경하는 것이 아니라 setter함수를 사용하여 변경해야한다.

예시코드에서는 Button 컴포넌트에 함수를 보냈으니 아래와같이 받을 것이다.

// Button.js
function Button({children, onClick}){
    return <button onClick={onClick}>{children}</button>;
}

export default Button;
  • 그렇기때문에 버튼을 누르면 부모컴포넌트의 handleRollClick함수가 실행되고, 함수내부의 setNum 으로 인해 주사위는 3이 된다.

참조형 state

배열이나 객체같은 참조형 state를 다룰 때에는 주의해야할 점이 있다.

아래 코드는 gameHistory라는 배열 state를 set하는 잘못된 예시이다.

import { useState } from 'react';
import Dice from "./Dice";
import Button from "./Buttom";

function random(n) {
    return Math.ceil(Math.random() * n);
}

function App(){
    const [num, setNum] = useState(1);
    const [sum, setSum] = useState(0);
    const [gameHistory, setGameHistory] = useState([]);

    const handleRollClick = () => {
        const nextNum = random(6);
        setNum(nextNum);
        setSum(sum + nextNum);
        gameHistory.push(nextNum);
        setGameHistory(gameHistory);
    }
    const handleClearClick = () => {
        setNum(1);
        setSum(0);
        setGameHistory([]);
    }
    return (
        <div>
            <div>
                <Button onClick={handleRollClick}>던지기</Button>
                <Button onClick={handleClearClick}>처음부터</Button>
            </div>
            <div>
                <h2>나</h2>
                <Dice color="blue" num={num}/>
                <h2>총점</h2>
                <p>{sum}</p>
            </div>
        </div>
    );
}
export default App;
  • handleRollClick 함수에서, 새로운 값을 배열(gameHistory)에 push한 후, 해당 배열을 set하는 모습을 볼 수 있다.
  • 하지만 gameHistory는 참조형 데이터이기 때문에 실제 값이 아닌 데이터가 저장된 주소값을 가진다.
  • 따라서 gameHistory.push 로 변수 내의 데이터를 변경해도, 주소값은 바뀌지 않았기때문에 react입장에서는 해당 state는 변화가 없다고 생각하여 화면을 다시 render하지 않는다.
    const handleRollClick = () => {
        const nextNum = random(6);
        setNum(nextNum);
        setSum(sum + nextNum);
        setGameHistory([…gameHistory, nextNum]);
    }
  • 해당 함수를 위와같이 스프레드구문을 사용해서 새 배열로 만들어준 후 인자로 전달해야 react에게 state가 바뀌었음을 알려줄 수 있다.

React가 렌더링하는 방식

React Component는 props와 state를 써서 데이터마다 다른 화면을 보여줄 수 있었다.

state가 바뀔 때 마다 다른 화면을 렌더링하는 방식을 알아보자.

 

1. state가 바뀌면 react는 return 부분을 통째로 재실행한다.

  • 문제점: 아무 변화가 없는 요소들도 다시 렌더링한다.
  • 이 문제를 해결하기위해 react는 return부분에 작성된 jsx를 바로 HTML dom에 매핑하지 않고, virtual dom을 활용한다.
  • virtual dom은 HTML domTree를 본딴 가상의 구조이다.

2. jsx를 virtual dom에 매핑한다.

  • 이 단계에서는 화면이 바뀌지 않는다. 화면을 다시 그릴 준비를 하는 과정이기 때문이다.

3. virtual dom과 HTML dom을 비교해 바뀐부분을 찾아낸다.

  • state의 변경 전 dom(HTML dom)과 state의 변경 후 dom(virtual dom)의 달라진 부분을 찾는다.

4. 바뀐 부분이 있다면, HTML dom node를 virtual dom node의 데이터로 새로 렌더링한다.

 

이렇게 virtual dom을 활용하면 아래와 같은 장점이 있다.

  1. 개발자가 직접 Dom 노드를 신경쓸 필요가 없기때문에 단순하고 깔끔한 코드를 작성할 있다.
  2. 변경사항을 리액트가 적당히 모아서 처리한다.
    리액트는 버츄얼돔이 바뀔때마다 브라우저에 바로 전달하지않고 적당히 모아뒀다가 브라우저에 전달한다.
    그래서 복잡한 페이지에서도 한정된 브라우저 자원을 효율적으로 활용할 있다. 변경사항을 효율적으로 처리할 있다.

virtual dom은 효율적으로 화면을 처리할 수 있기때문에 react만 가지는 것은 아니고, react와 비슷한 언어들은 대부분 갖고있다고 한다.

'React' 카테고리의 다른 글

React - Virtual DOM  (0) 2023.10.23
React - 데이터 다루기  (0) 2023.10.20
React - 예제 프로젝트 dicegame 만들기  (0) 2023.10.16
React - component and props  (0) 2023.10.16
React - jsx문법  (1) 2023.10.16