React Compiler は、React アプリを自動的に最適化する新しいビルド時ツールです。プレーンな JavaScript で動作し、React のルール を理解しているため、コードを書き直すことなく使用できます。

このページで学ぶこと

  • React Compiler の機能
  • React Compiler の導入方法
  • 段階的な導入戦略
  • 問題が発生した際のデバッグとトラブルシューティング
  • React ライブラリでのコンパイラの使用方法

補足

React Compiler は現在リリース候補 (RC) の状態です。皆様にコンパイラをお試しいただき、フィードバックを提供いただくことをお勧めします。最新のRCリリースは@rcタグで確認できます。

React Compiler の機能

React Compiler は、ビルド時に React アプリケーションを自動的に最適化します。React は最適化なしでも十分に高速ですが、アプリの応答性を保つために、コンポーネントや値を手動でメモ化する必要がある場合があります。このメモ化は面倒で、間違いやすく、コードのメンテナンス性を損ねます。React Compiler はこの最適化を自動的に行なってくれ、開発者は機能の構築に集中できるようになります。

React Compiler を使用しない場合

再レンダーを最適化するためにコンポーネントや値を手動でメモ化する必要があります。

import { useMemo, useCallback, memo } from 'react';

const ExpensiveComponent = memo(function ExpensiveComponent({ data, onClick }) {
const processedData = useMemo(() => {
return expensiveProcessing(data);
}, [data]);

const handleClick = useCallback((item) => {
onClick(item.id);
}, [onClick]);

return (
<div>
{processedData.map(item => (
<Item key={item.id} onClick={() => handleClick(item)} />
))}
</div>
);
});

React Compiler を使用する場合

手動のメモ化なしで同じコードを書くことができます。

function ExpensiveComponent({ data, onClick }) {
const processedData = expensiveProcessing(data);

const handleClick = (item) => {
onClick(item.id);
};

return (
<div>
{processedData.map(item => (
<Item key={item.id} onClick={() => handleClick(item)} />
))}
</div>
);
}

React Compiler Playground でこの例を確認

React Compiler は最適なメモ化を自動で適用し、必要なときだけ再レンダーされるようにします。

さらに深く知る

React Compiler はどのようなメモ化を行うのか?

React Compiler の自動メモ化は主に 更新パフォーマンスの向上(既存コンポーネントの再レンダー)に焦点を当てており、主に以下の 2 つのユースケースに重点を置いています:

  1. コンポーネントの連鎖的な再レンダーのスキップ
    • <Parent /> の再レンダーにより、<Parent /> のみが変更されたにも関わらず、そのコンポーネントツリー内の多くのコンポーネントが再レンダーされる
  2. React 外での高コストな計算のスキップ
    • 例えば、コンポーネントやフック内で expensivelyProcessAReallyLargeArrayOfObjects() を呼び出す場合

再レンダーの最適化

React では、UI を現在の state(具体的には:props、state、context)の関数として表現できます。現在の実装では、コンポーネントの state が変更されると、useMemo()useCallback()React.memo() による何らかのメモ化を適用していない限り、React はそのコンポーネント そのすべての子要素 を再レンダーします。例えば、以下の例では、<FriendList> の state が変更されるたびに <MessageButton> が再レンダーされます:

function FriendList({ friends }) {
const onlineCount = useFriendOnlineCount();
if (friends.length === 0) {
return <NoFriends />;
}
return (
<div>
<span>{onlineCount} online</span>
{friends.map((friend) => (
<FriendListCard key={friend.id} friend={friend} />
))}
<MessageButton />
</div>
);
}

React Compiler Playground でこの例を確認

React Compiler はメモ化と同等の処理を自動的に適用し、state が変更されてもアプリの関連部分のみが再レンダーされることを保証します。これは「細粒度リアクティビティ」と呼ばれることもあります。上記の例では、React Compiler は friends が変更されても <FriendListCard /> の返り値を再利用できると判断し、この JSX の再作成 カウントの変更による <MessageButton> の再レンダーを回避できます。

高コストな計算もメモ化される

React Compiler は、レンダー中に使用される高コストな計算も自動的にメモ化できます:

// **Not** memoized by React Compiler, since this is not a component or hook
function expensivelyProcessAReallyLargeArrayOfObjects() { /* ... */ }

// Memoized by React Compiler since this is a component
function TableContainer({ items }) {
// This function call would be memoized:
const data = expensivelyProcessAReallyLargeArrayOfObjects(items);
// ...
}

React Compiler Playground でこの例を確認

ただし、expensivelyProcessAReallyLargeArrayOfObjects が本当に高コストな関数である場合は、React 外で独自のメモ化を実装することを検討することをお勧めします。理由は以下の通りです:

  • React Compiler は React コンポーネントとフックのみをメモ化し、すべての関数をメモ化するわけではない
  • React Compiler のメモ化は複数のコンポーネントやフック間で共有されない

そのため、expensivelyProcessAReallyLargeArrayOfObjects が多くの異なるコンポーネントで使用される場合、同じ項目が渡されたとしても、その高コストな計算が繰り返し実行されます。コードを複雑にする前に、プロファイリング を行って、本当にそれほど高コストかどうかを確認することをお勧めします。

コンパイラを試すべきか?

すべての方に React Compiler の使用を開始することをお勧めします。コンパイラは現在も React のオプション機能ですが、将来的には一部の機能が完全に動作するためにコンパイラが必要になる可能性があります。

安全に使用できるか?

React Compiler は現在リリース候補 (RC) で、本番環境で広範囲にテストされています。Meta などの企業で本番環境で使用されていますが、あなたのアプリケーションでコンパイラを導入できるかは、コードベースの健全性と React のルール をどれだけ遵守できているかに依存します。

どのビルドツールがサポートされているか?

React Compiler は いくつかのビルドツール にインストールできます。Babel、Vite、Metro、Rsbuild などが含まれます。

React Compiler は主に、コアコンパイラを囲む軽量な Babel プラグインラッパーです。これは Babel 自体から分離されるように設計されました。コンパイラの最初の安定版は主に Babel プラグインとして残りますが、swc と oxc チームと協力して、React Compiler のファーストクラスサポートを構築しており、将来的にビルドパイプラインに Babel を追加する必要がなくなります。

Next.js を使用しているユーザは、v15.3.1 以降のバージョンを利用することで、SWC 経由(swc‑invoked)の React Compiler を有効化することができます。

useMemo、useCallback、React.memo をどう扱うべきか?

React Compiler を使用している場合、useMemouseCallbackReact.memo は不要になります。React Compiler はこれらのフックよりも正確で細かいメモ化を自動で追加します。手動のメモ化を保持することを選択した場合、React Compiler はそれらを分析し、手動のメモ化が自動的に推論されたメモ化と一致するかどうかを判断します。一致しない場合、コンパイラはそのコンポーネントの最適化を停止します。

これは慎重を期しての措置で、手動でのメモ化によくあるアンチパターンが、メモ化によって期待する挙動を担保しているからです。つまり、アプリケーションの動作が特定の値のメモ化に依存してしまう状態です。例えば、無限ループを防ぐ目的で、ある値をメモ化して useEffect の発火を抑えているケースなどです。これは React のルールに反しますが、コンパイラが手書きのメモ化を自動的に取り除くのは危険になり得るため、その場合は最適化を中止します。手書きのメモ化は自分で削除し、アプリケーションが期待どおりに動作するか確認してください。

React Compiler を試す

このセクションでは、React Compiler の始め方と、プロジェクトで効果的に使用するための情報を提供します。

追加リソース

これらのドキュメントに加えて、コンパイラに関する追加情報や議論については React Compiler Working Group を確認することをお勧めします。