OpenAIReact2025/10/01 0:00

React 19.2

要点だけを先に読めるように短く再構成したセクションです。

元記事

Quick Digest

要約

要点だけを先に読めるように短く再構成したセクションです。

openaijamodel: gpt-5-mini-2025-08-07

React 19.2 の主要アップデート

Key Points

  • Activity コンポーネント追加
  • useEffectEvent を導入
  • 部分プリレンダリング対応

Summary

React 19.2 が公開されました。主な追加は Activity コンポーネント(visible/hidden モード)や useEffectEvent、Server Components 向けの cacheSignal、Perf トラック、そして部分プリレンダリング(prerender / resume / resumeAndPrerender)です。SSR に関する改善(サスペンス境界のバッチング、Node での Web Streams 対応)や eslint-plugin-react-hooks v6、useId の既定プレフィックス変更などの互換性注意点もあります。

Key Points

  • Activity
    • hidden モードで子をアンマウントしつつ更新を後回しにでき、背景で次に必要な画面状態やリソースを準備可能。
    • visible モードは従来通り表示とエフェクトのマウントを行う。
  • useEffectEvent
    • エフェクト内で発火する "イベント" を分離して、最新の props/state を参照しつつエフェクトの再実行を防止。
    • eslint-plugin-react-hooks を最新版に上げて linter のサポートを得る必要あり。
  • cacheSignal(RSC 向け)
    • cache() の寿命終了を検知してフェッチの中止やクリーンアップに利用可能。
  • 部分プリレンダリング(Partial Pre-rendering)
    • prerender で prelude と postponed を取得し、後で resume / resumeAndPrerender で続きのレンダリングや静的出力を生成。
    • Node 実行環境ではパフォーマンスの観点から可能な限り Node Streams API(renderToPipeableStream 等)を推奨。
  • パフォーマンストラック
    • Chrome DevTools に Scheduler / Components トラックを追加。優先度やコンポーネント単位の作業を可視化。
  • SSR のサスペンス挙動改善
    • サーバーからのストリーミング時にサスペンスの reveal を短時間バッチングし、クライアント挙動と揃える(LCP 等の重要指標はヒューリスティックで保護)。
  • その他の変更
    • eslint-plugin-react-hooks@v6 の導入(flat config が既定)。レガシー設定に戻すオプションあり。
    • useId の既定プレフィックスを _r_ に変更(View Transition 対応のため)。

Migration / Practical notes

  • ライブラリ/プロジェクトで eslint-plugin-react-hooks を最新に上げ、useEffectEvent を使う箇所は linter による検証を有効にする。
  • Node 環境で SSR を行う場合は、可能なら既存の renderToPipeableStream / resumeToPipeableStream 系 API を維持してパフォーマンスを確保する。
  • 部分プリレンダリングを採用する場合は postponed state の保存・復元と CDN 配信フローを設計する。

参考

公式ドキュメントの各 API(Activity, useEffectEvent, cacheSignal, prerender/resume 系)と eslint-plugin-react-hooks のチェンジログを参照してください。

Full Translation

翻訳

原文の流れを保ったまま読める翻訳セクションです。

openaijamodel: gpt-5-mini-2025-08-07

React 19.2

React 19.2

公開日: 2025-10-01 作成者: The React Team

React 19.2 が npm で利用可能になりました!これは昨年に出た 3 回目のリリースで、前回は 19(12月)と 19.1(6月)です。本記事では React 19.2 の新機能の概要と注目すべき変更点を紹介します。

新しい React 機能

  • <Activity />
  • useEffectEvent
  • cacheSignal
  • Performance Tracks

<Activity />

<Activity> を使うとアプリを「アクティビティ」に分割し、制御や優先度付けができます。これはアプリの一部を条件付きレンダリングする代替として使えます。

例:

// Before
{ isVisible && <Page /> }

// After
<Activity mode={ isVisible ? 'visible' : 'hidden' }>
  <Page />
</Activity>

React 19.2 では Activity は visiblehidden の2つのモードをサポートします。

  • hidden: children を隠し、effects をアンマウントし、React にやることが残っていない間はすべての更新を延期します。
  • visible: children を表示し、effects をマウントし、更新を通常通り処理します。

これにより、画面に表示されているもののパフォーマンスに影響を与えずに、アプリの隠れた部分を事前レンダリングしてレンダリングを続けることが可能になります。ユーザーが次に移動しそうな隠れた部分を事前読み込みしたり、離れた部分の状態を保存しておくことで、データ・CSS・画像の事前読み込みや、戻るナビゲーション時の入力フィールドなどの状態保持が容易になります。

将来的には異なるユースケース向けにさらに多くのモードを追加する予定です。使用例は Activity docs を参照してください。

useEffectEvent

useEffect の一般的なパターンとして、外部システムからの「イベント」をアプリ側に通知することがあります。例えばチャットルームが接続したときに通知を表示するようなケースです。

問題: Effect の中で使う値(例: theme)が変わると Effect 全体が再実行されてしまい、意図しない再接続などが起こることがあります。多くの開発者は lint ルールを無効にして依存配列から除外しますが、それは後の変更でバグを招く可能性があります。

useEffectEvent を使うと、イベント部分のロジックを Effect 本体から切り離せます:

function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification('Connected!', theme);
  });

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on('connected', () => { onConnected(); });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);

  // ✅ 依存関係はすべて宣言されている(Effect Events は依存関係ではない)
  // ...
}

Effect Events は DOM イベントと同様に常に最新の props と state を参照します。Effect Events は依存配列に宣言すべきではありません。linter がそれらを依存関係として挿入しようとしないよう、eslint-plugin-react-hooks@latest にアップグレードする必要があります。

注意: Effect Events はその Effect と同じコンポーネントまたは Hook 内でしか宣言できません。これらの制約は linter によって検証されます。

いつ useEffectEvent を使うか

Effect から発火される「イベント」的な関数に対して useEffectEvent を使うべきです(これが "Effect Event" と呼ばれる理由です)。ただし、lint エラーを黙らせるためだけに何でも useEffectEvent で包む必要はなく、むしろそれでバグを招くことがあります。イベントとエフェクトを分離する考え方の詳細は: Separating Events from Effects を参照してください。

cacheSignal (React Server Components 向け)

cacheSignal は React Server Components 用です。cacheSignal を使うと cache() の寿命が終わったタイミングを知ることができます:

import { cache, cacheSignal } from 'react';
const dedupedFetch = cache(fetch);

async function Component() {
  await dedupedFetch(url, { signal: cacheSignal() });
}

これにより、キャッシュ内の結果がもはや使われなくなったときに作業をクリーンアップしたり中断したりできます。例えば:

  • React がレンダリングを正常に完了したとき
  • レンダリングが中断されたとき
  • レンダリングが失敗したとき

詳細は cacheSignal docs を参照してください。

Performance Tracks

React 19.2 は Chrome DevTools のパフォーマンスプロファイルにカスタムトラックを追加し、React アプリのパフォーマンスに関する情報をより詳しく提供します。詳細は React Performance Tracks docs を参照してください。ここではハイレベルの概要を示します。

  • Scheduler ⚛

    • Scheduler トラックは、ユーザー操作に対する "blocking" や startTransition 内の更新の "transition" など、異なる優先度で React が何に取り組んでいるかを示します。
    • 各トラック内で、どのような作業が行われているか(更新をスケジュールしたイベント、レンダリングがいつ行われたかなど)が表示されます。
    • 優先度によって更新がブロックされているときや、React が続行の前にペイントを待っているときの情報も表示されます。
  • Components ⚛

    • Components トラックは、React がレンダリングや effect の実行のために処理しているコンポーネントツリーを表示します。
    • "Mount"(children のマウントや effects のマウント)や、レンダリングが React 以外の作業に譲ってブロックされているときの "Blocked" などのラベルが表示されます。
    • コンポーネントのレンダリングや effect 実行のタイミングと所要時間を理解するのに役立ちます。

新しい React DOM 機能

Partial Pre-rendering

19.2 では、アプリの一部を事前にレンダリングし、後で再開(resume)して埋める機能を追加しました。これを "Partial Pre-rendering" と呼びます。静的な部分を CDN から配信し、後からシェルを再開して動的コンテンツを埋める用途に使えます。

事前レンダリングして後で再開するには、まず AbortController とともに prerender を呼び出します:

const { prelude, postponed } = await prerender(<App />, {
  signal: controller.signal,
});

// postponed state を保存しておく
await savePostponedState(postponed);
// prelude をクライアントや CDN に送る

その後、prelude をクライアントに返し、後で resume を呼んで SSR ストリームに再開します:

const postponed = await getPostponedState(request);
const resumeStream = await resume(<App />, postponed);
// ストリームをクライアントへ送る

あるいは resumeAndPrerender を呼んで SSG 用の静的 HTML を取得できます:

const postponedState = await getPostponedState(request);
const { prelude } = await resumeAndPrerender(<App />, postponedState);
// 完成した HTML prelude を CDN に送る

詳細は各 API のドキュメントを参照してください:

  • react-dom/server: resume(Web Streams 用)、resumeToPipeableStream(Node Streams 用)
  • react-dom/static: resumeAndPrerender(Web Streams 用)、resumeAndPrerenderToNodeStream(Node Streams 用)

さらに、prerender API は resume API に渡す postponed state を返すようになりました。

注目すべき変更点

Batching Suspense Boundaries for SSR

これまで Suspense 境界はクライアントでレンダリングされた場合とサーバー側ストリーミングで出力された場合で表示のされ方が異なる振る舞いをするバグがありました。19.2 では、サーバーレンダリングの Suspense 境界の reveal(落ちていたフォールバックから実際のコンテンツへの切り替え)を短時間バッチ処理するようにして、より多くのコンテンツをまとめて公開できるようにしました。これによりクライアントレンダリング時の振る舞いと整合させます。

以前はストリーミング SSR 時にサスペンスのコンテンツがすぐにフォールバックを置き換えていましたが、19.2 では短い時間だけバッチすることで、より多くのコンテンツをまとめて reveal できるようにします。この修正は SSR 中の Suspense 向けの <ViewTransition> サポートに備えるものでもあります。

注意: React はコア Web Vitals や検索ランキングに影響しないようヒューリスティックを用いてスロットリングを制御します。例えば合計ページロード時間が 2.5 秒(LCP の「良い」目安)に近づいている場合、React はバッチ処理を中止して即座にコンテンツを公開します。

SSR: Web Streams の Node サポート

React 19.2 は Node.js でのストリーミング SSR に対する Web Streams のサポートを追加しました:

  • renderToReadableStream が Node.js で利用可能に
  • prerender が Node.js で利用可能に
  • 新しい resume API(resume / resumeAndPrerender)が Node.js で利用可能に

注意点 — Node.js では Node Streams を優先する

Node.js 環境では引き続き Node Streams API を使うことを強く推奨します:

  • renderToPipeableStream
  • resumeToPipeableStream
  • prerenderToNodeStream
  • resumeAndPrerenderToNodeStream

理由: Node 上では Node Streams の方が Web Streams より高速で、さらに Web Streams はデフォルトで圧縮をサポートしていないため、意図せずストリーミングの利点を失う可能性があります。

eslint-plugin-react-hooks v6

eslint-plugin-react-hooks@latest を公開しました。recommended preset では flat config がデフォルトになり、React Compiler 由来の新しいルールはオプトインになっています。レガシーな設定を使い続けるには recommended-legacy に変更してください。

- extends: [ 'plugin:react-hooks/recommended' ]
+ extends: [ 'plugin:react-hooks/recommended-legacy' ]

Compiler が有効にするルールの完全な一覧は linter docs を参照してください。変更の詳細は eslint-plugin-react-hooks の changelog をご確認ください。

useId のデフォルトプレフィックスを更新

19.2 では useId のデフォルトプレフィックスを :r:(19.0.0)や «r»(19.1.0)から _r_ に変更しました。もともと CSS セレクタに無効な特殊文字を使うのはユーザーが書いた ID と衝突しにくくするためでしたが、View Transitions をサポートするために useId で生成される ID を view-transition-name や XML 1.0 の名前として有効にする必要がありました。

Changelog(抜粋)

その他の注目変更

  • react-dom: Allow nonce to be used on hoistable styles #32461
  • react-dom: Warn for using a React owned node as a Container if it also has text content #32774

重要なバグ修正

  • react: Stringify context as “SomeContext” instead of “SomeContext.Provider” #33507
  • react: Fix infinite useDeferredValue loop in popstate event #32821
  • react: Fix a bug when an initial value was passed to useDeferredValue #34376
  • react: Fix a crash when submitting forms with Client Actions #33055
  • react: Hide/unhide the content of dehydrated suspense boundaries if they resuspend #32900
  • react: Avoid stack overflow on wide trees during Hot Reload #34145
  • react: Improve component stacks in various places #33629, #33724, #32735, #33723
  • react: Fix a bug with React.use inside React.lazy-ed Component #33941
  • react-dom: Stop warning when ARIA 1.3 attributes are used #34264
  • react-dom: Fix a bug with deeply nested Suspense inside Suspense fallbacks #33467
  • react-dom: Avoid hanging when suspending after aborting while rendering #34192

完全な変更一覧は Changelog を参照してください。

Thanks to Ricky Hanlon for writing this post.