React 19.2が2025年10月1日にリリースされました。React 19(昨年12月)、React 19.1(今年6月)に続く、この1年で3回目となる重要なアップデートです。本記事では、レンダリングの制御を劇的に改善する <Activity /> や、エフェクトのロジックを最適化する useEffectEvent 、さらに次世代の描画手法である 部分プリレンダー (Partial Pre-rendering) など、開発者が押さえておくべき主要な変更点を詳しく解説します。
概要と技術的背景
React 19.2は、アプリケーションのパフォーマンス向上と、複雑なUI状態の管理をよりシンプルにすることに焦点を当てています。
最大級の目玉は、コンポーネントの表示・非表示をライフサイクルとともに制御する <Activity /> コンポーネントの導入です。また、これまでの useEffect で課題となっていた「依存関係による不要な再実行」を解決する useEffectEvent も追加されました。さらに、Chrome DevToolsとの連携による パフォーマンストラック の強化や、SSRにおける サスペンスバウンダリのバッチ処理 など、ユーザー体験(UX)を直接的に向上させる仕組みが多数盛り込まれています。
詳細解説
1. <Activity /> による表示制御の高度化
従来の {isVisible && <Page />} のような条件付きレンダーでは、非表示時にコンポーネントの状態(State)が失われていました。<Activity /> の mode="hidden" を使用すると、子要素を非表示にし、エフェクトをアンマウントしつつも Stateは保持 されます。また、Reactの処理が空くまで更新を遅延させるため、バックグラウンドでの事前読み込みや、タブ切り替え・「戻る」操作時の高速な復元が可能になります。
2. useEffectEvent でエフェクトを分離
特定の依存値(例:theme)が変更されただけで、エフェクト全体(例:チャット接続)が再実行される問題が解消されます。useEffectEvent を使うことで、最新のpropsやstateを参照しつつも、それ自体はエフェクトの依存配列に含める必要がない「イベント関数」として定義できます。なお、これを利用するには eslint-plugin-react-hooks@latest へのアップグレードが必要です。
3. cacheSignal によるクリーンアップ(RSC限定)
React Server Componentsにおいて、cache() のライフタイム終了を検知できる cacheSignal が導入されました。これにより、レンダーの完了・中断・失敗に合わせて、不要になったフェッチ処理などを適切にクリーンアップまたは中止できるようになります。
4. 部分プリレンダー (Partial Pre-rendering) の拡充
静的なシェル部分を事前にレンダーしてCDNから配信し、動的なコンテンツ部分を後からストリーミングで埋める 部分プリレンダー 用のAPI(prerender / resume)が整理されました。19.2では prerender が postpone ステートを返し、それを resume に渡すフローが確立されています。
5. SSRとNode.js環境の強化
- サスペンスのバッチ処理: SSR時に複数のサスペンスバウンダリが短時間で完了する場合、それらをまとめて表示(バッチ処理)することで、五月雨式な表示(レイアウトシフト感)を抑制します。LCP(最大視覚コンテンツの表示時間)への影響を避けるためのヒューリスティックも搭載されています。
- Node.jsでのWebストリームサポート:
renderToReadableStreamやprerenderがNode.jsでも利用可能になりました。ただし、パフォーマンスと圧縮の観点から、Node.js環境では引き続き NodeストリームAPI の使用が強く推奨されています。
6. 開発ツールとリンターの更新
- パフォーマンストラック: Chrome DevToolsに「Scheduler」と「Components」トラックが追加され、Reactがどの優先度で何を処理しているか、どのコンポーネントがブロックされているかを詳細に分析できるようになりました。
- eslint-plugin-react-hooks v6: デフォルトで Flat Config に対応しました。レガシー設定を維持する場合は
recommended-legacyを使用します。 useIdのプレフィックス変更: 生成されるIDの接頭辞が:r:から_r_に変更されました。これにより、CSSセレクタや View Transitions、XML 1.0の名前として有効な形式であることが保証されます。
クイック対談
エンジニアuseEffectEvent のおかげで、リンターの警告を無視することなく、特定のトリガーだけでエフェクトを動かせるようになるのは画期的だね。
デザイナー<Activity /> でバックグラウンド読み込みや状態維持が簡単になれば、画面遷移がもっと滑らかでリッチな体験になりそう!
実装・利用例
Activity の使用例
非表示中のStateを維持しながら、バックグラウンドでの事前読み込みを可能にします。
// 表示・非表示を切り替えても、Page内のState(入力内容など)は保持される
<Activity mode={isVisible ? 'visible' : 'hidden'}>
<Page />
</Activity>
useEffectEvent の使用例
依存配列から特定の値を排除し、意図しない再接続を防ぎます。
function ChatRoom({ roomId, theme }) {
// 最新のthemeを参照できるが、themeが変わってもこの関数自体は「不変」として扱われる
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme);
});
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
onConnected();
});
connection.connect();
return () => connection.disconnect();
}, [roomId]); // ✅ themeを依存配列に含める必要がない
}
cacheSignal の使用例(RSC)
import { cache, cacheSignal } from 'react';
const dedupedFetch = cache(fetch);
async function Component() {
// レンダーの中断や失敗時にリクエストを中止できる
await dedupedFetch(url, { signal: cacheSignal() });
}
部分プリレンダー (PPR) の基本フロー
サーバーサイドで静的部分(Prelude)と動的部分(Resume)を効率的に処理します。
// 1. 静的部分の事前レンダー(ビルド時やバックグラウンドで実行)
const { prelude, postponed } = await prerender(<App />, { signal });
// 2. クライアントのリクエストに応じて動的部分を再開(SSR時)
const resumeStream = await resume(<App />, postponed);
eslint-plugin-react-hooks v6 の設定
// eslint.config.js (Flat Config)
export default [
{
plugins: { 'react-hooks': reactHooksPlugin },
rules: reactHooksPlugin.configs.recommended.rules,
}
];
// レガシー設定 (.eslintrc) を維持する場合
// extends: ['plugin:react-hooks/recommended-legacy']
まとめ
<Activity />により、コンポーネントの状態を維持したまま、表示優先度の制御やバックグラウンド処理が可能になった。useEffectEventの導入で、エフェクトの再実行ロジックをより精密かつ安全に記述できるようになった。- 部分プリレンダー (PPR) や Node.jsでのWebストリームサポート により、SSR/SSGのパフォーマンスがさらに強化された。
useIdの仕様変更 や リンターのv6移行 など、既存プロジェクトのメンテナーが注意すべき変更も含まれている。