8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

React18のAutomaticBatchingを試してみた

Posted at

最近 React18beta がリリースされたので紹介していきます。

React18 の新機能

React18 で追加される大きな新機能は下記の通りです。

  • Concurrent Rendering(並行レンダリング)
  • SSR support for Suspense(サスペンスの SSR サポート)
  • Automatic Batching(自動バッチ処理)
  • Selective hydration(選択的ハイドレーション)
  • Built-in Cache(組み込みキャッシュ機構)

今回は自動バッチ処理を React17 と比較してみたいと思います。

インストール

create-react-app ${プロジェクト名}

npm install react@beta react-dom@beta

AutomaticBatching(自動バッチ処理)を検証

今までは関数内でのみバッチ処理を行っていました。
しかし、React18 からはどこから発生したか関係なくすべてのレンダリングがバッチ処理されるようになります。

creat-react-app のコードを流用して実装します。

React18 を導入するためには ReactDOM.render から ReactDOM.createRoot への移行が必須です。

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);



const container = document.getElementById("root");

const root = ReactDOM.createRoot(container);

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

検証用のコードです。
onClick で clickHandler 関数を実行します。
clickHandler 関数は setTimeout で非同期でステートを更新します。

const App = () => {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);
  const [count3, setCount3] = useState(0);

  const clickHandler = () => {
    setTimeout(() => {
      setCount1((count) => count + 1);
      setCount2((count) => count + 1);
      setCount3((count) => count + 1);
    }, 500);
  };

  console.log("rendering!!!");

  return (
    <div className="app">
      <header className="app-header">
        <img src={logo} className="app-logo" alt="logo" />
        <p>{count1}</p>
        <p>{count2}</p>
        <p>{count3}</p>
        <div className="push" onClick={clickHandler}>
          push
        </div>
      </header>
    </div>
  );
};

export default App;

以前の React であれば非同期処理内のステート更新は別々のレンダリングでした。

しかし、React18 ではバッチ処理が行われ、一回でレンダリングしているのがわかります。

  • React17
    react-17.gif

  • React18
    react-beta.gif

バッチ処理をオプトアウトできる API

flushSync でバッチ処理をオプトアウトできます。

import { flushSync } from "react-dom";

const App = () => {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);
  const [count3, setCount3] = useState(0);

  const clickHandler = () => {
    setTimeout(() => {
      // setCount1だけバッチ処理されない
      ReactDOM.flushSync(() => {
        setCount1((count) => count + 1);
      });
      setCount2((count) => count + 1);
      setCount3((count) => count + 1);
    }, 500);
  };

  console.log("rendering!!!");

  return (
    <div className="app">
      <header className="app-header">
        <img src={logo} className="app-logo" alt="logo" />
        <p>{count1}</p>
        <p>{count2}</p>
        <p>{count3}</p>
        <div className="push" onClick={clickHandler}>
          push
        </div>
      </header>
    </div>
  );
};

export default App;
  • React18
    react-beta2.gif

    flushSync のコールバック関数内に切り出せばバッチ処理されません。

さいごに

React 18 における並行レンダリングはオプトインになるため、コンポーネントの振る舞いにおいてデフォルトで大きな破壊的変更があるということはなくなります。
いつものメジャーリリースの時に要する労力と大差ないレベルの最小限の書き換えで、あるいは書き換えゼロで、React 18 にアップグレードすることができます。
「React 18に向けてのプラン」より引用

公式に書かれている通り StrictMode で実装していればほぼゼロコストでマイグレーションが可能です。破壊的変更(過去のコードに影響する変更)はオプトインになり、 Automatic Batching などのあまり影響のない変更はデフォルトで有効になるそうです。
React18 のアップデートするだけでもパフォーマンス向上が見込めるので、まだ一般向けリリースまで数か月ありますがアップデートに向けて準備しておくのもいいかもしれません。

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?