React Compiler 2026年版:自動メモ化の仕組みと面接対策の完全ガイド

React Compiler v1.0の自動メモ化の内部構造、コンパイルパイプライン、手動最適化が必要なケースを解説。2026年のReact技術面接で問われるポイントを網羅的にカバーします。

React Compiler 2026 automatic memoization interview questions

React Compilerは2025年10月に安定版v1.0に到達し、Reactアプリケーションにおけるパフォーマンス最適化のあり方を根本的に変えました。2026年の技術面接では、コンパイラの内部構造、メモ化戦略、そして手動最適化が依然として必要なシナリオを理解していることが求められます。

面接のポイント

React Compilerは静的解析を通じてコンポーネントとフックを自動的にメモ化するビルドタイムツールです。ほとんどの場合、useMemouseCallbackReact.memoの手動記述が不要になりますが、すべてのケースに対応できるわけではありません。面接官は、コンパイラが処理できることとできないことの両方を明確に説明できることを期待しています。

React Compilerとは何か:コードをどのように最適化するのか

React Compilerは、Reactのコンポーネントとフックを解析し、有効な箇所にきめ細かいメモ化を挿入するビルドタイム最適化コンパイラです。useMemouseCallbackによる手動メモ化とは異なり、コンパイラは条件付きかつより細かい粒度でメモ化を行うことができます。これは開発者が手動で維持するのは現実的ではないレベルの最適化です。

コンパイラはBabelプラグインとして動作します(Next.js 15.3.1以降ではSWCサポートも拡大中)。各コンポーネントを独立して処理し、Babel ASTをカスタムの高水準中間表現(HIR)に変換した後、複数の最適化パスを実行します。

Metaでの本番環境の結果がその効果を裏付けています。Quest Storeでは初回読み込みが最大12%高速化し、インタラクションは2.5倍以上速くなりました。Sanity Studioでは1,231個のコンパイル済みコンポーネントで20-30%のレンダリング時間削減が報告されています。

面接回答の構成: コンパイラを定義し、ビルドタイム(ランタイムではない)で動作することを説明し、HIRパイプラインに言及し、実際のメトリクスを引用します。

コンパイルパイプライン:ソースコードから最適化済みコードへ

この質問は深い理解を試すものです。React Compilerのパイプラインには7つの明確なフェーズがあります。

  1. ソースからASTへ — BabelがJavaScript/TypeScriptを抽象構文木(AST)にパースする
  2. ASTからHIRへ — コンパイラがASTをReactのコンポーネントモデル専用に設計された高水準中間表現に変換する
  3. SSA変換 — 静的単一代入形式により、各変数が正確に一度だけ代入されるようにし、精密なデータフロー追跡を可能にする
  4. 型推論とエフェクト分析 — コンパイラが操作をその影響によって分類する:read、store、capture、mutate、freeze
  5. リアクティブ分析 — すべての式がstatic(定数、インポート)またはreactive(props、state、contextから派生)に分類される
  6. スコープ発見 — コンパイラが独立してキャッシュおよび無効化できる独立したメモ化スコープを特定する
  7. コード生成 — 自動メモ化を含む最適化されたJavaScriptが出力される
javascript
// コンパイル前
function UserProfile({ user, theme }) {
  const fullName = user.firstName + ' ' + user.lastName;
  const style = { color: theme.primary, fontSize: 16 };
  return <div style={style}>{fullName}</div>;
}

// コンパイル後(簡略化した表現)
function UserProfile({ user, theme }) {
  const $ = useMemoCache(4);
  let fullName;
  if ($[0] !== user.firstName || $[1] !== user.lastName) {
    fullName = user.firstName + ' ' + user.lastName;
    $[0] = user.firstName;
    $[1] = user.lastName;
    $[2] = fullName;
  } else {
    fullName = $[2];
  }
  // styleとJSXも同様にメモ化...
}

面接で重要なポイントは、コンパイラがコンポーネントレベルではなく値レベルでメモ化を行うことです。各計算値は独自のキャッシュスロットを持ち、その特定の依存関係が変更された場合のみ無効化されます。この粒度は、実際のコードでuseMemoが達成するレベルを超えています。

Reactのルール:コンパイラが前提とするもの

React Compilerは、コンポーネントがReactのルールに従っているという前提のもとで静的解析を行います。これらのルールに違反するコードに対しては、コンパイラはそのコンポーネントの最適化を静かにスキップします。これは面接で頻繁に問われるトピックです。

3つの核となるルール:

  • コンポーネントとフックは純粋でなければならない — 同じ入力に対してレンダーは同じ出力を生成する必要がある。レンダー中に外部のミュータブルな状態を読み書きしてはならない。
  • フックは呼び出し規約に従わなければならない — フックを条件分岐内、ループ内、またはネストされた関数内で呼び出すことはできない。
  • PropsとStateはレンダー内で不変である — propsやstateを直接変更してはならず、常にセッター関数を使用する。
javascript
// ルール違反:レンダー中のミューテーション
function BadCounter({ items }) {
  // コンパイラはこのコンポーネントをスキップする
  items.sort(); // propsの配列をミューテートしている!
  return <ul>{items.map(i => <li key={i}>{i}</li>)}</ul>;
}

// 正しい実装:新しいソート済み配列を作成
function GoodCounter({ items }) {
  const sorted = [...items].sort();
  return <ul>{sorted.map(i => <li key={i}>{i}</li>)}</ul>;
}
面接でよくある落とし穴

コンパイラがコンポーネントをスキップしても、エラーは発生しません。アプリケーションは正常に動作しますが、そのコンポーネントは最適化されません。ESLintプラグイン(eslint-plugin-react-hooksrecommendedプリセット)が、本番環境に到達する前にこれらの違反を検出する主要な方法です。

ESLint統合:ビルド前に違反を検出する

スタンドアロンのeslint-plugin-react-compilerパッケージは2025年後半に非推奨となりました。コンパイラ関連のすべてのリントルールはeslint-plugin-react-hooks@latestに統合されています。

ESLintフラットコンフィグでのセットアップ:

eslint.config.jsjavascript
import reactHooks from 'eslint-plugin-react-hooks';
import { defineConfig } from 'eslint/config';

export default defineConfig([
  reactHooks.configs.flat.recommended,
]);

コンパイラベースのルールが検出する3つの重要なパターン:

  • set-state-in-render — レンダー中にsetStateを呼び出し、無限ループを引き起こす
  • set-state-in-effect — エフェクト内の高コストな計算がカスケードする再レンダーを引き起こす
  • refs — レンダー中にrefの.currentにアクセスする(refはミュータブルなエスケープハッチであり、レンダーセーフではない)

面接では、推奨される導入戦略を説明できることが重要です。まずESLintルールを有効にし、すべての違反を修正してから、コンパイラを有効にします。この段階的なアプローチにより、予期しない問題を最小限に抑えることができます。

手動最適化が依然として必要なケース

コンパイラはすべてのパフォーマンス作業を排除するわけではありません。いくつかのシナリオでは手動介入が必要です。

1. 外部ストアのサブスクリプション

コンパイラはReactのstate、props、contextのみを追跡します。外部ストア(Redux、Zustand、MobX)にはuseSyncExternalStoreまたはストア独自のセレクタ機構が必要です。

2. 大規模データセットの高コストな計算

コンパイラは戻り値をメモ化しますが、実行コストのプロファイリングは行いません。10万件のアイテムをフィルタリングする関数は、依存関係が変更されるたびに実行されます。本当に高コストな操作には、明示的な依存関係を持つuseMemoが引き続き有効です(ただし、コンパイラがメモ化を重複させることはありません)。

3. Refベースの命令的ロジック

Refはコンパイラのリアクティブモデルから意図的に除外されています。アニメーション、canvas描画、サードパーティDOMライブラリの統合には手動制御が必要です。

4. コンポーネント間のメモ化

コンパイラは単一のコンポーネントまたはフック内でメモ化を行います。コンポーネント間で計算値を共有するには、stateの引き上げ、context、または外部キャッシュレイヤーが必要です。

javascript
// コンパイラが自動的に処理するケース
function ProductCard({ product, currency }) {
  const formattedPrice = formatCurrency(product.price, currency);
  const discount = product.originalPrice - product.price;
  return (
    <div>
      <span>{formattedPrice}</span>
      {discount > 0 && <Badge>Save {discount}</Badge>}
    </div>
  );
}

// 手動最適化が必要なケース:高コストな外部計算
function DataGrid({ rows }) {
  const processedRows = useMemo(
    () => rows.map(row => heavyTransform(row)), // O(n) with large n
    [rows]
  );
  return <VirtualList items={processedRows} />;
}

React / Next.jsの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

フレームワーク別のReact Compiler有効化方法

面接では、ツールチェーンのセットアップに関する実践的な知識も試されます。

Next.js(15.3.1以降): SWCネイティブサポート。next.config.jsで有効化します:

next.config.jsjavascript
const nextConfig = {
  experimental: {
    reactCompiler: true,
  },
};
export default nextConfig;

Vite 8 + @vitejs/plugin-react v6: v6が内部Babelを廃止しoxcに移行したため、コンパイラには@rolldown/plugin-babelが必要です:

vite.config.jsjavascript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import babel from '@rolldown/plugin-babel';
import { reactCompilerPreset } from 'babel-plugin-react-compiler';

export default defineConfig({
  plugins: [
    react(),
    babel({ presets: [reactCompilerPreset] }),
  ],
});

Expo SDK 54以降: デフォルトで有効です。設定は不要です。

バージョン互換性

React Compiler v1.0はReact 17、18、19で動作します。ただし、完全な最適化機能はReact 19でのみ利用可能であり、ランタイムがuseMemoCacheを通じたきめ細かいキャッシュ無効化をサポートしています。

React Compiler vs 手動メモ化:面接用比較表

| 観点 | React Compiler | 手動(useMemo/useCallback) | |------|---------------|----------------------------------| | 粒度 | 値レベル、式ごと | ブロックレベル、開発者が定義 | | 条件付きメモ化 | サポート | 不可能 | | メンテナンスコスト | ゼロ — 自動 | 高い — 依存配列、ラッパーコンポーネント | | コンポーネント間 | 単一コンポーネントスコープ | 単一コンポーネントスコープ | | 外部ストア | 追跡しない | 追跡しない(useSyncExternalStoreを使用) | | クラスコンポーネント | サポートしない | N/A(React.memoを使用) | | ビルド要件 | Babelプラグインまたは SWC | なし | | パフォーマンス上限 | 再レンダー20-60%削減(典型的) | 開発者のスキルに依存 |

面接での要約:コンパイラは、開発者が以前手動で行っていたメモ化の80-90%を、より細かい粒度とゼロのメンテナンスオーバーヘッドで自動化します。残りの10-20%は、外部統合、高コストな計算、命令的DOM操作に関わるものです。

よくある面接質問と簡潔な回答

Q: React Compilerはビルドタイムで動作するのか、ランタイムで動作するのか? ビルドタイムです。コンパイラはコンパイル中にソースコードを変換するBabelプラグインです。最適化された出力はキャッシュ値を保存するためのランタイムヘルパー(useMemoCache)を使用しますが、すべての解析はデプロイ前に行われます。

Q: 既存のuseMemouseCallbackの呼び出しは削除すべきか? Reactチームは既存の手動メモ化をそのまま残すことを推奨しています。削除すると、コンパイル出力が微妙に変わる可能性があります。新しいコードではコンパイラに頼り、プロファイリングで特定の必要性が確認された場合にのみ手動メモ化を追加します。

Q: コンパイラが最適化できないコードに遭遇した場合はどうなるか? そのコンポーネントまたはフックを静かにスキップします。アプリケーションは正常に動作しますが、その部分は自動メモ化なしで実行されます。ESLintルールが開発中にこれらのケースを表面化させます。

Q: コンパイラはServer Componentsとどのように相互作用するのか? Server Componentsはサーバー上で実行され、シリアライズされた出力を生成します。クライアントで再レンダーされることはありません。コンパイラは再レンダーが主要なパフォーマンス上の懸念となるクライアントコンポーネントを対象とします。この2つの最適化は補完的です。Server Componentsがバンドルサイズを削減し、コンパイラがクライアントの再レンダーコストを削減します。

Q: コンパイラはカスタムフックを最適化できるか? はい。コンパイラはコンポーネントとフックの両方を解析します。派生状態を計算するカスタムフックの戻り値は自動的にメモ化され、そのフックを呼び出すすべてのコンポーネントに恩恵をもたらします。

まとめ

  • React Compiler v1.0は、7フェーズのコンパイルパイプライン(AST、HIR、SSA、型推論、エフェクト分析、リアクティブ分析、コード生成)を通じて、きめ細かい値レベルのメモ化を挿入するビルドタイムツールである
  • コンパイラはコンポーネントがReactのルールに従っていることを前提とし、違反があるとサイレントにオプトアウトする(eslint-plugin-react-hooksrecommendedプリセットで検出可能)
  • 外部ストアのサブスクリプション、大規模データセットの高コスト計算、Refベースの命令的ロジック、コンポーネント間キャッシュには手動最適化が依然として必要
  • フレームワーク別セットアップ:Next.jsではSWCネイティブ、Vite 8では@rolldown/plugin-babel、Expo SDK 54以降ではデフォルト有効
  • 面接では、コンパイラが自動化する部分(メモ化の80-90%)と自動化しない部分(外部state、高コストなコンポーネント間計算、クラスコンポーネント)の両方を明確に説明できることが重要
  • 推奨される導入パスは、まずESLintルールを有効にし、次にコンパイラを有効にする段階的戦略であり、面接官はこのアプローチについて質問することが予想される

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

タグ

#react
#react-compiler
#memoization
#performance
#interview

共有

関連記事