📑

React18のAuto Batchingの確認

2022/04/01に公開約1,500字

背景

Auto Batchingがどのように起こるか確認する。状態変更のたびにレンダリングしていたのが1回になるという言及しているものがあったが、これはもともと1回のはずである。非同期処理で行うと複数回起きてしまうはずである。認識が正しいか確認したい。

先に、まとめ

イベントハンドラで複数回状態変更したときにReactが複数回レンダリングするかどうかを確認した。
React17ではsetTimeoutなどで非同期にして状態変更をすると複数回レンダリングしてしまうが、React18では行われなくなっていることが確認できた。

バージョン 変更タイミング レンダリング回数
React17 同期 1回だけ
React17 非同期 複数回
React18 同期 1回だけ
React18 非同期 1回だけ

React18の非同期実行時に明示的にレンダリングを行いたい場合はflushSyncを使う。
補足として、StrictModeを使うとReactElementの作成が複数回起きてしまう点には注意。

確認方法

React18とReact17で確認できるコードを用意した。
内容はこんな感じ(JSXの部分は省略)

  const handleClick = () => {
    setA((a) => a + 1);
    setB((b) => b + 2); // React17でも1度だけ。
  };
  // 別のボタンから呼び出す。setTimeoutを使っている。
  const handleAsyncClick = () => {
    setTimeout(() => {
      setA((a) => a + 1); // React17だとここでレンダリングが発生してしまう
      setB((b) => b + 2);
    }, 0);
  };

  useEffect(() => {
    console.log("effect");
  });

  console.log("render", { a, b });

React18での動作確認用

https://codesandbox.io/s/auto-battching-test-in-18-gf1jts?file=/src/App.js

React17での動作確認用

https://codesandbox.io/s/auto-battching-test-in-17-3ur1lr?file=/src/App.js

React17と同じようにしたい場合は以下のようにする。

  const handleFlushSyncClick = () => {
    setTimeout(() => {
      flushSync(() => {
        setA((a) => a + 1);
      });
      flushSync(() => {
        setB((b) => b + 2);
      });
    }, 0);
  };

–--

このドキュメントは "Chatwork Tech Tips" の一環です。
その他の Tips も含めて @Chatwork_dev にて確認することができます 👨‍🏫

Discussion

ログインするとコメントできます