blog.s2n.tech

React v18でReact Compilerを使う&一部のコンポーネントだけReact Compilerで最適化する方法

公開

はじめに

先日、React Compilerがオープンソースになり、誰でも利用できるようになりました。しかしながら、React CompilerはReactのベータ版が必要になり、本番環境でガッツリ利用するのは少し難しいです。

そこで、今回はReact CompilerをReact v18でも利用できるようにする方法を紹介します。また、一度に全てのコンポーネントをReact Compilerで最適化するのは、リスクを伴うため、一部のコンポーネントだけをReact Compilerで最適化する方法も紹介します。

なお、React Compiler自体がまだベータ版であることに留意してください。

React Compilerとは

React Compilerはコンポーネントやフックを自動的に最適化するためのツールです。具体的にはコンポーネントやフックに含まれる値をメモ化し、不要な再レンダリングを抑制することで、パフォーマンス向上が期待できます。

今まではメモ化するためにユーザーが手動でuseMemo, useCallback, React.memoなどを使う必要がありましたが、React Compilerを使うことで、これらの手間を省くことができます。

React Compilerはその名の通り、コンパイラとして動作します。具体的にはビルドツールと連携することで、ユーザーが書いたコードそのものを変換します。

現在はBabelのプラグインとしてのみ提供されていますが、今後はRustでの実装になるようです。1

React Compiler Playgroundでコンポーネントのコードをコンパイルした例

React Compilerの導入方法

公式ドキュメントに記載の通りですが、一応導入方法を紹介します。なお、今回はViteを使います。

まず、React CompilerのBabel実装であるbabel-plugin-react-compilerをインストールします。

Terminal window
npm install -D babel-plugin-react-compiler

次に、vite.config.jsに以下のように設定します。

export default defineConfig({
plugins: [
react({
babel: {
plugins: [["babel-plugin-react-compiler", {}]],
},
}),
],
});

"babel-plugin-react-compiler"の隣の空オブジェクトはオプションです。React 18でReact Compilerを使う場合は追加の設定が必要なので、後で解説します。 Reactのbeta版を使う場合はこれで設定は完了です。

React v18でReact Compilerを使う

ここからはReact v18でReact Compilerを使うための設定方法を紹介します。まず、React Compilerではコンパイル後のコードにcという関数が追加されるため、それを追加する必要があります。2

c関数はReactのbeta版には含まれていますが、React 18には含まれていません。 babel-plugin-react-compilerでは自分で定義したc関数を使うように設定することができるため、まずは自分でc関数を定義します。

import { useState } from "react";
const $empty = Symbol.for("react.memo_cache_sentinel");
/**
* DANGER: this hook is NEVER meant to be called directly!
*
* Note that this is a temporary userspace implementation of this function from
* React 19. It is not as efficient and may invalidate more frequently than the
* official API. Please upgrade to React 19 as soon as you can.
*/
export function c(size: number) {
return useState(() => {
const $ = new Array(size);
for (let ii = 0; ii < size; ii++) {
$[ii] = $empty;
}
// @ts-ignore
$[$empty] = true;
return $;
})[0];
}

このc関数はこちらのGistからお借りしました。なおこれはReactのベータに含まれるreact-compiler-runtimeと同じです。

これをsrc/react-compiler-runtime.tsとして保存します。後でパスを指定しますが、ファイル名や場所は自由です。

次に、vite.config.jsを以下のように修正します。

export default defineConfig({
plugins: [
react({
babel: {
plugins: [
[
"babel-plugin-react-compiler",
{
runtimeModule: path.resolve("src/react-compiler-runtime"),
},
],
],
},
}),
],
});

runtimeModuleオプションに先ほど作成したsrc/react-compiler-runtime.tsのパスを指定します。これでReact Compilerがビルド時に自分で定義したc関数を使うようになります。

これでReact 18でもReact Compilerを使う準備が整いました。後はいつも通りにビルドすれば、React Compilerがコンパイル時にコンポーネントを最適化してくれます。

なお執筆現在、@vite/plugin-react v4.3.0にはruntimeModuleオプションを指定した際にエラーが発生するバグがあります。v4.2.1にダウングレードすることで回避可能です。 詳細はこちらのIssueを参照してください。(PRも立っているので、そのうち修正されると思います)

一部のコンポーネントだけReact Compilerで最適化する

さて、ここまではReact Compilerを動かす方法を紹介してきました。しかし、一度に全てのコンポーネントをReact Compilerで最適化するのはリスクが伴います。特に、React Compilerはまだベータ版であり、いきなり全てのコンポーネントを最適化すると予期せぬバグが発生する可能性があります。一応、React CompilerはReactのルールに則っているコンポーネント・フックのみを最適化するようになっていますが、それでもリスクはゼロではありません。

徐々にReact Compilerを導入していくためにbabel-plugin-react-compilerではコンパイラの最適化をOpt-inにすることができます。まずは、vite.config.jsの設定を以下のように変更します。

export default defineConfig({
plugins: [
react({
babel: {
plugins: [
[
"babel-plugin-react-compiler",
{
runtimeModule: path.resolve("src/react-compiler-runtime"),
compilationMode: "annotation",
},
],
],
},
}),
],
});

compilationModeオプションに"annotation"を指定することで、React Compilerの最適化をOpt-inにすることができます。このオプションを設定すると、コンポーネントに"use memo"というディレクティブを追加しないとReact Compilerが最適化を行いません。

function MyComponent() {
"use memo";
return <div>"Hello, world!"</div>;
}

このように、コンポーネントの先頭に"use memo";と書くことで、そのコンポーネントだけをReact Compilerで最適化することができます。これによって、徐々にReact Compilerを導入していくことができます。なお、このディレクティブはReact Compilerの移行期にのみ使うように想定されており、長期的な利用は推奨されていません。

詳しくは公式ドキュメントを参照してください。

まとめ

今回はReact v18でReact Compilerを使う方法と、一部のコンポーネントだけをReact Compilerで最適化する方法を紹介しました。 React Compilerはまだベータ版であり、本番環境で利用するのはリスクが伴いますが、徐々に導入していくことでパフォーマンス向上が期待できます。

また、React Compilerの中身を@yossydev@retaroと一緒に読む動画も公開しているので、興味があればぜひご覧ください。

React Compiler Code reading #1

参考

注釈

  1. 現に、Reactのリポジトリにはbabelの実装コードと、Rustの実装コード(まだ未完成)が存在しています。 ↩︎

  2. 実際には_cという関数になっていますが、ベータ版に含まれるc関数の実装であるreact-compiler-runtimeにはcとして定義されているため、ここではcとして説明します。 ↩︎