FrontEnd

React Compilerを使ってみた

投稿日:

はじめに

React 19と同時に話題になっていたReact Compilerが2025年10月に1.0として安定版になり、Next.js 16では reactCompiler オプションがexperimentalから昇格して正式サポートになりました。

useMemouseCallback を手で書かなくてよくなる」という事をNext.jsのサンプルプロジェクトで実際に試してみました。

環境は以下の通りです。

  • Next.js 16.3.5
  • React 19.3.0
  • babel-plugin-react-compiler 1.0.0

React Compilerとは

React Compilerは、コンポーネントとフックのコードをビルド時に解析し、自動でメモ化コードを差し込むBabelプラグインです。

これまで再レンダリングの最適化は、開発者が useMemo / useCallback / React.memo を「どこに入れるべきか」を判断して手書きしていました。React Compilerを使うと、ビルド時に useMemo / useCallback / React.memo に相当するメモ化コードをコンパイラが自動で差し込みます。開発者はどこにメモ化を入れるかを判断する必要がなくなり、ソースコードにはこれらを書かなくて済みます。

Next.jsの中でReact Compilerはどう動いているのか

React Compilerは「Babelプラグイン」として提供されていますが、Next.jsの標準コンパイラはBabelではなくSWCです。この2つの関係を整理しておきます。

BabelとSWC

どちらもJavaScript / TypeScriptのトランスパイラです。ブラウザはJSXやTypeScriptをそのまま実行できないので、ビルド時に次のような変換が必要になります。

Babel SWC
実装言語 JavaScript Rust
速度 遅い Babelの数倍〜数十倍速い
プラグイン JSで書けるので豊富 Babel用プラグインは動かない
Next.jsでの扱い v11までの標準 v12から標準

React CompilerはReactチームがBabelプラグインとして開発してきたため、パッケージ名が babel-plugin-react-compiler になっているようです。

Next.js 12以降の本体はSWCでビルドしていますが、React CompilerはBabelプラグインなので、SWCからそのまま呼ぶことはできません。そこで以下のように2段構えになっているようです。

  1. 通常のトランスパイルはSWCで高速に処理する
  2. reactCompiler: true のとき、JSXやReact Hooksを含むファイルだけをSWCが判別し、そのファイルにだけBabelを起動して babel-plugin-react-compiler を適用する

全ファイルにBabelをかけるより速いものの、対象ファイルの分だけBabel(Node.js上のJS処理)が走るので、React Compilerを有効にするとビルドは多少遅くなります。ドキュメントにも「デフォルトのRustベースのコンパイラと比べるとわずかに遅くなることはあるが、影響は小さく局所的」の旨が書かれています。

導入

まずBabelプラグインをインストールします。

次に next.config.ts で有効化します。

実際に動かしてみる(検証)

検証用コンポーネント

親コンポーネントに「テキスト入力」と「数値n」の2つのstateを持たせ、nだけを受け取る重い子コンポーネント HeavyChild を置きました。

テキスト入力は HeavyChild とは無関係なstateなので、文字を打っても HeavyChild が再レンダリングされないことが期待する挙動です。

useMemouseCallbackReact.memo も書いていません。 heavyCount はnに比例して時間がかかるので、ボタンでnを100ずつ増やすと再計算のコストが増えていきます。

無関係なstateを更新したとき、重い子は再描画されるか

テキスト入力に a b c と3文字打ったときのコンソール出力を、 reactCompiler のon/offで比較しました。

reactCompiler: false

1文字打つたびに HeavyChild が再レンダリングされ、そのたびに heavyCount(100) を計算し直しています。これが、手動でメモ化していないコードの「普通の」挙動です。

reactCompiler: true

3文字打っても 重い子コンポーネント render は1回も出ません。親の CompilerDemo はstateが変わるので再レンダリングされますが、子の HeavyChild はスキップされています。

「子コンポーネントの値を増やす」ボタンを押すと、 n が変わるので HeavyChild は再計算されます。

再レンダリングされない仕組み

React Compilerが差し込むメモ化コードの仕組みは次のとおりです。

  • コンポーネント内で計算される値やJSXを、依存する値ごとに分解する
  • それぞれをキャッシュ用のスロット( useMemoCache )に保存し、依存値が変わっていなければ前回の結果を再利用する
  • 依存値が変わったときだけ、その部分を再計算する

これを、 HeavyChildnext build の成果物( .next/static/chunks/*.js )でどう変わっているかで確認します。成果物はバンドル・minifyされた1行のコードなので、読みやすい形に整形し、説明のために (1)(4) のコメントを付けています。

有効化前の子コンポーネント

React Compiler有効化前のビルド成果物は、ソースコードをそのまま変換したものです。

有効化後の子コンポーネント

React Compiler有効化後は次のようになります。 x がpropsの nh は計測用の開始時刻です。

  • (1) useMemoCache(17) で、このコンポーネント専用のキャッシュ配列を確保します。スロット数はコンポーネント内で分解された値の数で決まり、 HeavyChild では17個です
  • (2) heavyCount の呼び出しが「propsの n が前回と違うときだけ」になっています。手書きの useMemo(() => heavyCount(n), [n]) と同等のコードが自動生成されたことになります。今回のコードでは console.log も同じブロックに入っているので、 n が同じときはログ自体が出ません
  • (3) 計算結果だけでなく、 <p><div> のJSX要素も依存値ごとにキャッシュされます。依存値が同じなら、新しい要素を作らずに前回のオブジェクトを取り出します
  • (4) その結果、 n が同じときの return u は、前回のレンダーで返したものと同一の要素オブジェクトを返します。Reactは前回と同一のオブジェクト(参照が同じ)を受け取ると、その下の差分比較もDOM更新も行いません

親の CompilerDemo 側も同じ形にコンパイルされていて、 <HeavyChild n={n} /> の要素オブジェクトが親のstate n に対してキャッシュされます。 n が同じなら親が再レンダリングされても前回と同一のオブジェクトが渡されるため、Reactは HeavyChild 関数を呼びません。これが React.memo 相当の効果となります。

手書きの memo / useMemo と比べると

React Compilerが無ければ、開発者が React.memouseMemo を次のように手で書いて同じ効果を実現します。

Rules of Reactを守っていないコンポーネントはスキップされる

React Compilerは、Rules of React(Reactのルール)を守っていないコンポーネントをコンパイル対象からスキップします(壊しはしません)。最適化されないまま従来通り動きます。

"use no memo" でコンポーネント単位にオプトアウトする

特定のコンポーネントだけ最適化の対象外にしたいケースのために "use no memo" ディレクティブが用意されています。例えば以下のように書くと HeavyChild だけがキャッシュなしの素のコードになります。

さいごに

next.config.ts に1行足すだけで、 useMemo / useCallback / React.memo を書いていないコードから、手書き相当のメモ化コードが生成されることを確認できました。この機能はかなり便利だと思うので、今後は積極的に使用の検討をしようと思います。

blog-page_footer_336




blog-page_footer_336




-FrontEnd
-,

執筆者:

免責事項

このブログは、記事上部に記載のある投稿日時点の一般的な情報を提供するものであり、投資等の勧誘・法的・税務上の助言を提供するものではありません。仮想通貨の投資・損益計算は複雑であり、個々の取引状況や法律の変更によって異なる可能性があります。ブログに記載された情報は参考程度のものであり、特定の状況に基づいた行動の決定には専門家の助言を求めることをお勧めします。当ブログの情報に基づいた行動に関連して生じた損失やリスクについて、筆者は責任を負いかねます。最新の法律や税務情報を確認し、必要に応じて専門家に相談することをお勧めします。


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

react-icon

【初級者向け】Reactの開発環境の構築

1 はじめに2 Reactとは2.1 Declarative2.2 Component-Based2.3 Learn Once, Write Anyway2.4 Reactのメリット、デメリット3 環 ...

Vue.js入門その1〜基本文法〜

はじめに 軽量JSフレームワークとして有名なVue.js。 最近、Laravelに触れる機会が増えたことと、以前からRails + Vueという構築を耳にするので、今更ではありますが勉強を始めようと思 ...

Vue.js入門その6〜RouterとComponentを使ってTODOアプリを修正〜

1 はじめに2 vue-routerのインストール3 サーバーサイドの改修3.1 APIに詳細(show)を追加3.2 元となるビューファイルを作成3.3 ルーティングの修正4 Vue.jsの実装4. ...

ざっとPWAの概要を調べてみる

1 はじめに2 ProgressiveWebAppsとは3 PWAに必要な技術4 PWAとセットでよく出てくる「AMP」とは5 ネイティブアプリ vs PWA6 さいごに はじめに ここ最近、Andr ...

react-icon

React Konvaで状態管理されたCanvasを描画してみた

1 はじめに2 Canvasとは?3 React Konvaとは4 着せ替えアプリっぽいサンプルを作成4.1 React Konvaの導入4.2 画像の描画4.3 stateによるCanvas描画4. ...

フォロー

blog-page_side_responsive

2026年9月
 12345
6789101112
13141516171819
20212223242526
27282930  

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。