React 19.2リリース!ActivityやuseEffectEventなど新機能を徹底解説

目次

React 19.2 が 2025年10月1日にリリースされました。React 19(2024年12月)、React 19.1(2025年6月)に続く、この1年で3度目の重要アップデートです。今回の焦点は大きく3つ——コンポーネントの表示制御の高度化エフェクトの依存関係管理の改善、そしてSSRパフォーマンスの強化です。

目玉機能である <Activity />useEffectEvent はどちらも「既存の書き方の限界を解消する」という性格を持っており、日常的な実装パターンを見直す良い機会になります。本記事では、各機能が生まれた背景から実務での活用方法まで体系的に解説します。

技術的背景:React 19.2 が解決する課題

React 19.2 の変更を理解する上で重要なのは、それぞれの機能が「どんな問題を解決しようとしているか」を把握することです。

{isVisible && <Page />} のような条件付きレンダーは、非表示にすると State が失われるという根本的な制約がありました。モーダルやタブ、「戻る」操作での高速復元など、State を保ちつつ非表示にしたいユースケースには対応できていませんでした。また useEffect の依存配列は、「このトリガーで実行したいが、この値の変化では再実行してほしくない」という細かい制御が難しく、リンター警告を回避するために意図しない実装を余儀なくされるケースが頻繁にありました。React 19.2 はこれらの実務上の痛点を正面から解決しています。

新機能 詳細解説

1. <Activity />:State を保ったまま非表示にする

課題: 従来の条件付きレンダー({isVisible && <Page />})では、false になった瞬間にコンポーネントがアンマウントされ、入力内容やスクロール位置などの State がリセットされていました。再表示のたびにデータを再フェッチする必要もあり、UX とパフォーマンスの両面で課題でした。

解決策: <Activity mode="hidden"> は子コンポーネントを非表示にしつつ State を保持します。エフェクトはアンマウント時と同様に切断されますが、State は内部に残り、再表示時にはゼロから再構築されることなく高速に復元されます。さらに、バックグラウンドでの事前レンダーも可能なため、タブ切り替えや「戻る」操作が劇的にスムーズになります。

// 従来:非表示にすると State がリセットされる
{isVisible && }

// Activity:非表示でも State(入力内容・スクロール位置など)は保持される

  

タブ UI やマルチステップフォーム、スライドパネルなど「一時的に隠すが状態は残したい」コンポーネントに特に有効です。ただし非表示中も State はメモリに保持されるため、大量のデータを抱えるコンポーネントでは不要になった時点での明示的なクリーンアップも検討してください。

2. useEffectEvent:エフェクトの「トリガー」と「参照」を分離する

課題: useEffect では「このトリガー(roomId の変更)でだけ再実行したいが、最新の theme は参照したい」という場合に問題が生じます。theme を依存配列に含めるとテーマ変更のたびにチャットが再接続され、省略するとリンター警告が出て、// eslint-disable コメントで黙らせるという不健全な対処に陥りがちでした。

解決策: useEffectEvent を使うと、最新の値を参照しながらも依存配列に含める必要がない「イベント関数」を定義できます。エフェクトのトリガー条件(依存配列)と、エフェクト内で参照したい最新の値を明確に分離できます。利用には eslint-plugin-react-hooks@latest へのアップグレードが必要です。

function ChatRoom({ roomId, theme }) {
  // useEffectEvent で定義した関数は「常に最新の theme を参照」するが、
  // それ自体はエフェクトの依存配列に含める必要がない
  const onConnected = useEffectEvent(() => {
    showNotification('Connected!', theme); // 最新の theme を参照できる
  });

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on('connected', () => {
      onConnected(); // theme が変わっても再接続は発生しない
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]); // ✅ roomId だけが再接続のトリガー。theme は不要
}

useEffectEvent で定義した関数はエフェクトの外部で呼び出せません。これは「エフェクト内でのみ使う副作用のイベントハンドラ」という意味付けを強制し、意図しない使い方を型レベルで防ぐ設計です。

3. cacheSignal:RSC でのフェッチをライフタイムに連動してキャンセル

課題: React Server Components でキャッシュされたフェッチ処理は、レンダーが中断・失敗しても処理がそのまま継続されていました。不要なネットワークリソースが消費され続ける問題がありました。

解決策: cacheSignal()cache() のライフタイムに紐づいた AbortSignal を返します。fetchsignal オプションに渡すことで、レンダーの完了・中断・失敗に合わせてリクエストを自動的にキャンセルできます。

import { cache, cacheSignal } from 'react';

const dedupedFetch = cache(fetch);

async function Component() {
  // cacheSignal() をシグナルとして渡すことで、
  // レンダーが中断・失敗した際にフェッチを自動キャンセル
  const data = await dedupedFetch(url, { signal: cacheSignal() });
  return 
{data.title}
; }

4. 部分プリレンダー(PPR):静的と動的を効率的に組み合わせる

背景: 部分プリレンダーは「静的なシェル部分をビルド時に生成して CDN から即座に配信し、動的コンテンツはリクエスト時にストリーミングで埋める」というアーキテクチャです。従来の SSG(全部静的)と SSR(全部動的)の中間を取る手法です。

19.2 では prerenderresume の API が整理され、フローが確立されました。prerender が静的部分(Prelude)と、動的部分の再開に必要な情報(postponed)を返し、それを resume に渡すことでリクエスト時の処理が完成します。

// ビルド時 / バックグラウンド:静的部分を事前レンダー
const { prelude, postponed } = await prerender(, { signal });
// prelude → CDN にキャッシュして即座に配信
// postponed → 動的部分を再開するために必要な情報

// リクエスト時:動的部分をストリーミングで補完
const resumeStream = await resume(, postponed);

5. SSR の強化:サスペンスのバッチ処理と Node.js の Web ストリームサポート

サスペンスのバッチ処理では、SSR 時に複数のサスペンスバウンダリが短時間で解決する場合、それらをまとめてフラッシュします。これにより「五月雨式な表示更新によるレイアウトシフト感」を抑制します。ただし LCP(最大視覚コンテンツの表示時間)への影響を避けるためのヒューリスティックが内部で動いており、重要なコンテンツの表示が遅れないよう調整されています。

Node.js での Web ストリームサポートとして、renderToReadableStreamprerender が Node.js でも利用できるようになりました。ただし、パフォーマンスと圧縮効率の観点から、Node.js 環境では引き続き Node.js ストリーム APIrenderToPipeableStream)の使用が推奨されています。Web ストリームへの対応は、エッジランタイムとのコード共有を想定したユースケース向けと理解しておきましょう。

6. 開発ツールとリンターの更新:既存プロジェクトへの影響を確認

パフォーマンストラック(Chrome DevTools 連携)では、「Scheduler」と「Components」トラックが追加されました。React がどの優先度で何を処理しているか、どのコンポーネントがレンダリングをブロックしているかを Timeline ビューで把握できます。useEffectEvent の実行タイミング確認や、<Activity /> のバックグラウンド処理の観察に活用できます。

eslint-plugin-react-hooks v6 では、Flat Config(eslint.config.js)がデフォルトになりました。既存プロジェクトで .eslintrc を使い続ける場合は recommended-legacy に切り替える必要があります。また、useEffectEvent の正しい使い方を検証するルールが追加されているため、useEffectEvent を利用する際はこのバージョンへのアップグレードが必須です。

useId のプレフィックス変更として、生成される ID の接頭辞が :r: から _r_ に変わりました。:r0: のような形式は CSS セレクタとして無効(コロンは疑似クラスと解釈される)でしたが、_r0_ は CSS・View Transitions・XML 1.0 のすべてで有効な識別子になります。スナップショットテストや E2E テストで useId の出力値を直接参照している場合は更新が必要です。

// eslint.config.js(Flat Config)
export default [
  {
    plugins: { 'react-hooks': reactHooksPlugin },
    rules: reactHooksPlugin.configs.recommended.rules,
  }
];

// .eslintrc を使い続ける場合(レガシー設定)
// extends: ['plugin:react-hooks/recommended-legacy']
エンジニア

useEffectEvent のおかげで、// eslint-disable-next-line に頼らずに「このトリガーでだけ動かしたい」という意図をコードで正確に表現できるようになりましたね。依存配列の設計がずっとクリアになります。

デザイナー

<Activity /> でタブ切り替え時の入力内容が保持されるようになれば、ユーザーが「あれ、さっき入力した内容が消えた」と感じる場面がぐっと減りますね。画面遷移の体験が自然に向上しそうです。

移行時の注意点まとめ

既存プロジェクトを React 19.2 に移行する際に確認すべき点を整理します。

  • useEffectEvent を使う場合eslint-plugin-react-hooks@latest(v6)へのアップグレードが必須。
  • ESLint の設定形式を確認し、.eslintrc を継続使用するなら recommended-legacy に切り替える。
  • useId の出力値:r0:_r0_)を参照しているスナップショットテストや E2E テストは更新が必要。
  • Node.js 環境での SSR に Web ストリーム API を使っている場合は、パフォーマンスの観点から Node.js ストリーム API への移行を検討する。

まとめ

React 19.2 は「既存の書き方の限界を根本から解消する」アップデートです。改めてポイントを整理します。

  • <Activity />:State を保ったまま非表示にでき、タブ切り替えや「戻る」操作の高速復元とバックグラウンド事前レンダーが実現する。
  • useEffectEvent:エフェクトの「再実行トリガー」と「参照したい最新値」を明確に分離でき、依存配列の設計が直感的になる。
  • cacheSignal(RSC 限定):キャッシュのライフタイムに連動してフェッチを自動キャンセルし、不要なリソース消費を防ぐ。
  • 部分プリレンダー(PPR)prerender / resume API が整理され、静的配信と動的ストリーミングを組み合わせた高速な SSR アーキテクチャが構築しやすくなった。
  • 移行時の注意点useId の出力値変更と ESLint v6 への対応は、既存プロジェクトで影響が出やすいため早めに確認を。

新機能の中でも <Activity />useEffectEvent は、日常の実装パターンを直接改善するものです。まず既存コードで「条件付きレンダーで State が消える問題」や「依存配列の整理に苦労している箇所」を探してみることが、最も効率的な導入の第一歩になるはずです。

コメントを残す

入力エリアすべてが必須項目です。メールアドレスが公開されることはありません。

内容をご確認の上、送信してください。