Featured image of post React Compiler (React Forget) がもたらす手動メモ化からの解放 Featured image of post React Compiler (React Forget) がもたらす手動メモ化からの解放

React Compiler (React Forget) がもたらす手動メモ化からの解放

React Compiler(旧称React Forget)による自動メモ化の仕組みとメリットを解説。useMemoやuseCallbackの手動最適化を不要にし、ビルド時に自動で依存関係を分析してレンダリング最適化する次世代コンパイラの動作原理と実践的な導入方法を紹介します。

はじめに

長年にわたり、React開発者はuseMemouseCallbackReact.memoを駆使して手動でレンダリングを最適化してきました。React Compiler(旧称 React Forget)はこのパラダイムを根本から変えます。ビルド時に自動的にリアクティブな依存関係を分析し、コンポーネントやフック、値を自動でメモ化することで、手動最適化フックを不要にします。


手動メモ化の問題点

典型的なフィルタリングリストの例を見てみましょう。

function UserList({ users, filter }) {
  const filtered = useMemo(
    () => users.filter(u => u.name.includes(filter)),
    [users, filter]
  );

  const handleClick = useCallback((id) => {
    console.log('Clicked:', id);
  }, []);

  return (
    <ul>
      {filtered.map(user => (
        <UserRow key={user.id} user={user} onClick={handleClick} />
      ))}
    </ul>
  );
}

const UserRow = React.memo(({ user, onClick }) => (
  <li onClick={() => onClick(user.id)}>{user.name}</li>
));

このコードは冗長であり、依存配列の誤りや記述忘れが発生しやすいという問題があります。useMemoを忘れれば不要な再レンダリングが発生し、依存配列が誤ればステイルクロージャを生みます。


React Compilerとは

React CompilerはBabel / SWCのビルド時プラグインで、Reactコンポーネントのリアクティビティモデルを自動的に理解し、メモ化ロジックをコードに埋め込みます。

// 開発者が書くコード
function Welcome({ name }) {
  const greeting = `Hello, ${name}!`;
  return <p>{greeting}</p>;
}
// コンパイラが出力するコード(概念)
function Welcome({ name }) {
  const $ = React.cache(/* ... */);
  let greeting = $.refresh('greeting', name => `Hello, ${name}!`, [name]);
  return $.memo('element', () => <p>{greeting}</p>, [greeting]);
}

コンパイラはどの値がpropsやstate、その他のリアクティブな値に依存するかを検出し、その依存関係が変更されたときのみ再計算します。


動作の仕組み

  1. リアクティブ値の検出 — ASTを走査し、props、state、フック、その他のリアクティブソースから派生する変数を特定します。

  2. 依存関係グラフの構築 — リアクティブ値を結ぶ依存関係グラフを構築します。非リアクティブな値から派生するものは静的とみなされます。

  3. メモ化コードの生成 — キャッシュ無効化ロジックを含む最適化されたJavaScriptを出力します。

ルール動作
propsとstateは常にリアクティブ派生値は自動でメモ化
非リアクティブな定数はhoistされるオーバーヘッドなし
コールバックはデフォルトで安定useCallback不要
コンポーネント本体は最適化リアクティブな依存のみ再実行

ビルドツールとの統合

Vite

import react from '@vitejs/plugin-react';

export default {
  plugins: [
    react({
      babel: {
        plugins: [['babel-plugin-react-compiler', { target: '18' }]],
      },
    }),
  ],
};

Next.js

// next.config.js
module.exports = {
  experimental: {
    reactCompiler: true,
  },
};

不要になるもの

React Compilerを有効にすると以下は原則不要になります。

  • useMemo() — 値の自動メモ化
  • useCallback() — 関数の自動安定化
  • React.memo() — コンポーネントの自動メモ化
  • 手動の依存配列(ほとんどのケース)

注意点とベストプラクティス

注意点推奨対応
可変refuseRefの書き換えは追跡対象外useStateに置き換え、または明示的な再レンダリング
外部ストア — React外の値useSyncExternalStoreでラップ
複雑な副作用 — 非リアクティブな依存を含むuseEffect必要に応じて明示的な依存を追加
レガシーコンテキスト — クラスコンポーネントのcontextモダンなuseContextに移行

まとめ

React Compilerは手動から自動へのメモ化パラダイムシフトを象徴します。ビルド時に依存関係を分析することで、パフォーマンスバグとボイラープレートコードの両方を排除します。既存プロジェクトへの導入は最小限の設定で済み、新規プロジェクトでは「コンパイラが最適化を任せる」という新しい考え方でコードを書けるようになります。