렌더링 최적화를 위한 Automatic Batching (React 18)

hzn·2023년 4월 11일
1

React

목록 보기
15/15
post-thumbnail

배칭(Batching)이란?

  • 리액트가 더 나은 성능을 위해 여러 개의 state 업데이트를 하나의 리렌더링 (re-render)로 묶는 것
  • 불필요한 리렌더링을 줄이기 때문에 성능에 좋다.

[React 18 이전] 이벤트 핸들러 내부에서 발생하는 state 업데이트 ➡️ 배칭 ✅

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

  function handleClick() {
    setCount((c) => c + 1); // 아직 리렌더링 X
    setFlag((f) => !f); // 아직 리렌더링 X
    // 함수가 끝나면 리렌더링 (배칭!)
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style=>{count}</h1>
    </div>
  );
}
  • 그러나 React 18 이전에는 언제 배칭하는지가 일관적이지 못했다.
  • React 18 이전에는 이벤트 핸들러 밖에서 발생하는 (= 이벤트 진행 중에 발생한 것이 아닌) 업데이트는 배칭하지 않았다.

[React 18 이전] 이벤트 핸들러 밖에서 발생하는 state 업데이트 ➡️ 배칭 ❌

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

  function handleClick() {
    fetchSomething().then(() => {
      // 배칭 X
      setCount((c) => c + 1); // 리렌더링 발생
      setFlag((f) => !f); // 리렌더링 발생
    });
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style=>{count}</h1>
    </div>
  );
}
  • 이벤트가 진행되는 중이 아닌, 완료된 후의 콜백에서 state 업데이트가 발생하기 때문에 배칭되지 않는다.

자동 배칭이란?

  • React 18의 createRoot를 통해, 어떤 이벤트에서 왔는지와 상관없이 모든 업데이트들이 자동 배칭된다.
  • (createRoot가 아닌 레거시 render를 사용할 경우 자동 배칭이 적용되지 않는다.)

[React 18 이후] 이벤트 핸들러 밖에서 발생하는 state 업데이트 ➡️ 배칭 ✅

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

  function handleClick() {
    fetchSomething().then(() => {
      setCount((c) => c + 1);
      setFlag((f) => !f);
      // 이벤트 핸들링이 끝나고 콜백이 끝나면 리렌더링 (배칭!)
    });
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style=>{count}</h1>
    </div>
  );
}
  • 이벤트 진행 중이 아닌, 이벤트 핸들링이 끝난 후 콜백에서 state 업데이트가 일어나고 있지만 배칭이 적용된다.

배칭을 하고 싶지 않다면?

  • ReactDOM.flushSync()를 사용하면 배칭을 하지 않고 state 변경 즉시 렌더(DOM 업데이트)가 이루어진다.
  • 일반적인 상황에서는 잘 쓰이지 않는다.
import { flushSync } from "react-dom"; // Note: react가 아닌 react-dom이다

function handleClick() {
  flushSync(() => {
    setCounter((c) => c + 1);
  });
  // DOM 업데이트
  flushSync(() => {
    setFlag((f) => !f);
  });
  // DOM 업데이트
}

레퍼런스

0개의 댓글