state, useState

sumjo
|2024. 3. 6. 01:36

리액트의 state는 일반 자바스크립트 객체이고 랜더링을 유발하는 변수라고 볼 수 있다.

state 값이 변경되면 리액트는 해당 컴포넌트를 다시 랜더링 한다.

 

useState() 함수를 통해 state를 사용할 수 있다.

useState는 배열을 반환하는데 [state, setFunction] 형태이다. useState의 인자는 state의 초기값이다.

여기서 setFunction은 state의 값을 변경하는 데 사용된다.

import React, { useState } from 'react';

function ToggleComponent() {
  // useState를 사용하여 상태 초기화
  const [state, setState] = useState('yes'); // 초기 상태는 'yes'

  // 상태를 'no'로 업데이트하는 함수
  const changeStateCorrectly = () => {
    setState('no'); // 올바른 방법: setState를 사용하여 상태 업데이트
  };

  // 잘못된 방법으로 상태를 변경하려고 시도하는 함수 (실제로는 작동하지 않음)
  const changeStateIncorrectly = () => {
    state = 'no'; // 잘못된 방법: 직접 상태를 수정하려고 시도
    console.log(state); // 이 코드는 실제로는 React 상태 업데이트 원칙에 어긋나며, 실행되지 않음
  };

  return (
    <div>
      <p>Current state: {state}</p>
      <button onClick={changeStateCorrectly}>Change State Correctly</button>
      <button onClick={changeStateIncorrectly}>Change State Incorrectly (Won't Work)</button>
    </div>
  );
}

export default ToggleComponent;

 

위에 changeStateIncorrectly에서 state 값에 직접 할당을 하는 방식은 오류가 난다.

state는 직접 변경할 수 없고 useState에서 반환받은 setState를 통해 업데이트 해야 한다. state는 기본적으로 Immutable 객체이다.

 

리액트는 setState가 호출되면 랜더링큐에 컴포넌트가 들어가게 된다. 함수형 컴포넌트라면 함수의 반환값이 jsx 컴포넌트로 변화 되는 것이다. 리액트는 가상돔 트리를 만들어 이전의 트리와 차이를 비교한다.

여기서 state의 '차이'를 감지할 때 값이 아닌 콜스택의 주소값을 비교 즉 '얕은 비교'를 통해 변화를 인지한다.

때문에 state는 불변성을 지켜야 빠른 랜더링과 사이드 이펙트를 방지할 수 있다.

 

출처 : https://velog.io/@mollog/React%EC%97%90%EC%84%9C%EC%9D%98-%EA%B0%80%EC%83%81%EB%8F%94-%EA%B0%9C%EB%85%90

기존의 가상 돔과 변화된 버전의 가상돔을 비교하고 차이점을 commit한 후 다시 브라우저에 적용하는 모습이다.

 재귀적으로 만들어진 트리이기 때문에 하위 컴포넌트들도 자동으로 다시 호출되고 하위 컴포넌트도 변화를 감지한다.

 

import React, { useState } from 'react';

function CounterExample() {
  // useState를 사용하여 count 상태를 초기화
  const [count, setCount] = useState(0);

  function handleClick() {
    // 첫 번째 setCount는 현재 count 값(스냅샷)에 1을 더한다.
    setCount(count + 1);
    // 두 번째 setCount는 동일한 스냅샷(count의 현재 값)에 1을 더한다.
    setCount(count + 1);
    // 세 번째 setCount도 동일하게 처리된다.
    setCount(count + 1);
  }

  return (
    <div>
      <button onClick={handleClick}>Increase Count</button>
      <p>Count: {count}</p>
    </div>
  );
}

export default CounterExample;

 

state는 스냅샷으로 제공된다.

떄문에 setCount에서 count + 1을 하는 모든 count 는 스냅샷을 찍었던 시점의 값으로 저장된다.

리액트는 batcing을 통해 state를 변경하는 작업을 일괄적으로 처리한다.