はじめに
長年にわたり、React開発者はuseMemo、useCallback、React.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、その他のリアクティブな値に依存するかを検出し、その依存関係が変更されたときのみ再計算します。
動作の仕組み
リアクティブ値の検出 — ASTを走査し、props、state、フック、その他のリアクティブソースから派生する変数を特定します。
依存関係グラフの構築 — リアクティブ値を結ぶ依存関係グラフを構築します。非リアクティブな値から派生するものは静的とみなされます。
メモ化コードの生成 — キャッシュ無効化ロジックを含む最適化された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()— コンポーネントの自動メモ化- 手動の依存配列(ほとんどのケース)
注意点とベストプラクティス
| 注意点 | 推奨対応 |
|---|---|
可変ref — useRefの書き換えは追跡対象外 | useStateに置き換え、または明示的な再レンダリング |
| 外部ストア — React外の値 | useSyncExternalStoreでラップ |
複雑な副作用 — 非リアクティブな依存を含むuseEffect | 必要に応じて明示的な依存を追加 |
| レガシーコンテキスト — クラスコンポーネントのcontext | モダンなuseContextに移行 |
まとめ
React Compilerは手動から自動へのメモ化パラダイムシフトを象徴します。ビルド時に依存関係を分析することで、パフォーマンスバグとボイラープレートコードの両方を排除します。既存プロジェクトへの導入は最小限の設定で済み、新規プロジェクトでは「コンパイラが最適化を任せる」という新しい考え方でコードを書けるようになります。
