TIL #25 | React에서 배치 업데이트를 사용하는 이유와 문제점

kibi·2023년 11월 14일
1

TIL (Today I Learned)

목록 보기
24/83
import { useState } from "react";

function App() {
  const [count, setCount] = useState(0);

  function handleClick() {
    // 첫 번째 상태 업데이트
    setCount(count + 1);
    console.log(count); // 이 시점에서 상태는 아직 업데이트되지 않았습니다.

    // 두 번째 상태 업데이트
    setCount(count + 1);
    console.log(count); // 이 시점에서도 상태는 아직 업데이트되지 않았습니다.
  }

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

export default App;

위와 같은 코드를 실행할 경우

count는 업데이트 이전의 값인 0으로 출력된다.
-> handleClick() 함수 안에서 출력되는 count 값은 업데이트 이전의 값이기 때문에 0으로 출력되기 때문이다.
또한, 리액트는 이벤트 핸들러 안에서 발생하는 모든 상태 변경을 한꺼번에 처리하기 때문에 화면에 반영되기 전까지 변경된 상태를 볼 수 없다.
이러한 특성은 성능 최적화를 위해 사용되며 리액트가 불필요한 렌더링을 줄일 수 있도록 도와준다.

배치 업데이트?

렌더링을 하기 위해선 state의 변경이 수행되어야 한다.
state 하나가 변경될 때마다 화면에 반영한다면 매우 많은 'layout-shift'가 일어난다.
-> 리액트는 이러한 상황에서 발생하는 모든 상태 변경을 한 번에 수집하여 단일 렌더링으로 처리한다!

프로세스
state 변경(상태 변경) -> virtual DOM 생성 -> diffing -> reconciliation

virtual DOM
실제 DOM과 내용을 공유하는 복사본

diffing
렌더링 이전 버전의 가상돔과 업데이트 이후 변경될 가상돔을 비교한다.
(리액트는 항상 이 두가지의 가상돔을 함께 갖는다.)

reconciliation
가상돔에서 변경된 사항을 실제돔에 한꺼번에 반영한다. => 배치 업데이트

배치 업데이트를 할 경우 비동기적으로 업데이트 되기 때문에 상태가 즉시 업데이트 되지 않는다는 문제점이 있다.

배치 업데이트의 문제점 해결 방법

useEffect + 함수형 업데이트

(1) useEffect

  useEffect(() => {
    console.log("useEffect");
    console.log(count);
  }, [count]);

바로 반영되지 않았던 문제 해결

(2) 함수형 업데이트

  function handleClick() {
    // 첫 번째 상태 업데이트
    setCount((prev) => prev + 1);
    console.log(count); // 이 시점에서 상태는 아직 업데이트되지 않았습니다.

    // 두 번째 상태 업데이트
    setCount((prev) => prev + 1);
    console.log(count); // 이 시점에서도 상태는 아직 업데이트되지 않았습니다.
  }

배치 업데이트로 인해 setState 하나만 반영되었던 문제 해결

  • setState(콜백함수) 의 형태로 사용하면 반환값이 바로 반영되어 다음 setState에서 변경된 값을 받을 수 있다.

import { useEffect, useState } from "react";

function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("useEffect");
    console.log(count);
  }, [count]);

  function handleClick() {
    // 첫 번째 상태 업데이트
    setCount((prev) => prev + 1);
    console.log(count); // 이 시점에서 상태는 아직 업데이트되지 않았습니다.

    // 두 번째 상태 업데이트
    setCount((prev) => prev + 1);
    console.log(count); // 이 시점에서도 상태는 아직 업데이트되지 않았습니다.
  }

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

export default App;

0개의 댓글