React

React 19.2の新機能|Activity・useEffectEvent・cacheSignal・PPRの使い方と19.2.8までの修正

React 19.2の新機能|Activity・useEffectEvent・cacheSignal・PPRの使い方と19.2.8までの修正

React 19.2は2025年10月1日にnpmで公開されたマイナーリリースです。画面の一部を状態ごと隠す <Activity>、Effectから呼ぶイベント処理を依存配列から切り離す useEffectEvent、Server Components用の cacheSignal、ChromeのPerformanceパネルに出るPerformance Tracks、静的部分を先に作るPartial Pre-renderingが入りました。

19.2系のパッチは2026年7月21日の19.2.8まで出ています。2026年9月9日には19.3.0が公開されました。Reactの最新バージョンと18からの変更点はReact 19とは?18からの変更点・新機能と最新バージョンの解説で扱っています。この記事は19.2で入った機能に絞り、react/react-dom 19.2.8と19.3.0をNode.js 26.5.0とjsdomで動かした結果を載せます。

まとめ:React 19.2で押さえる要点

  • <Activity mode="hidden"> は子のEffectを片付け、DOMを display: none で隠し、stateを残します。表示に戻すとEffectが再実行されます。
  • useEffectEvent で作った関数は依存配列に入れず、Effectの中からだけ呼びます。レンダー中に呼ぶと実行時エラーになります。
  • 19.2系では、memo・forwardRef で包んだコンポーネント内の useEffectEvent がContextの古い値を読みます。19.2.8でも再現し、修正は19.3.0に入りました。
  • cacheSignal はServer Components専用です。Client Componentsでは常に null を返します。
  • 19.2.1〜19.2.8の8本はすべてReact Server Componentsの修正です。RSCを使う構成でDoS対策まで入っているのは19.2.4以降です。
  • 互換性に効く変更は useId の形式(«r0» から _r_0_)と、ESLintプラグインの既定がflat configになった点です。

以下、機能の一覧、各APIの使い方と実行結果、SSRまわりの変更、19.2.xのパッチ、19.3へ上げる判断の順に説明します。

React 19.2で追加された機能の一覧

機能 パッケージ 使える場所 役割
<Activity> react クライアント・SSR 状態を残して隠す
useEffectEvent react Effectの中 最新値を読むイベント関数
cacheSignal react Server Componentsのみ cache終了でAbortSignal発火
Performance Tracks react-dom 開発・プロファイリングビルド DevToolsに専用トラック
resume など計4API react-dom サーバー Partial Pre-rendering
Suspenseのバッチ表示 react-dom ストリーミングSSR 表示をまとめて行う
Node.jsでWeb Streams react-dom Node.js renderToReadableStream 等が利用可

19.1.9をインストールして確かめると、Activity も useEffectEvent も react からexportされていません。useEffectEvent を呼ぶと TypeError: ... useEffectEvent is not a function で止まります。上の機能を使うには19.2.0以上が必要です。

<Activity>の使い方:hidden時のEffect停止とstate保持

条件付きレンダリング({isVisible && <Page />})で消すと、コンポーネントはアンマウントされ、入力途中の値も消えます。<Activity> に置き換えると、隠している間もstateとDOMが残ります。mode は 'visible' と 'hidden' の2つで、省略すると 'visible' です。

import { Activity, useEffect, useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log('effect mount');
    return () => console.log('effect cleanup');
  }, []);
  return (
    <>
      <span>{count}</span>
      <button onClick={() => setCount(c => c + 1)}>加算</button>
    </>
  );
}

export function Tab({ isVisible }) {
  return (
    <Activity mode={isVisible ? 'visible' : 'hidden'}>
      <Counter />
    </Activity>
  );
}

19.2.8で、countを5にしてから hidden に切り替えると、effect cleanup が出力され、<span> は <span style="display: none !important;">5</span> になりました。visible に戻すと effect mount が再び出て、表示は5のままです。19.3.0でも同じ結果でした。Effectは止まり、stateとDOMは残る、という動きです。

Activityで隠すときに注意が要る子コンポーネント

DOMそのものが副作用を持つ子は、隠されることを前提にクリーンアップを書く必要があります。hidden はDOMを破棄しないため、<video> を再生したまま隠すと音声が流れ続けます。React公式ドキュメントのトラブルシューティングはこの例を挙げ、Effectのクリーンアップで pause() を呼ぶ対処を示しています。ほかに注意点が2つあります。テキストだけを返す子は、隠す要素が無いため hidden の間は何も出力されません。隠した状態での事前読み込みは、use で読むPromiseのようにSuspenseを起動するデータだけが対象で、Effectの中のfetchは先読みされません。

useEffectEventの使い方:Effectの依存配列から外す値の書き分け

useEffectEvent は、Effectから呼ばれる「イベント」部分を切り出すフックです。React公式ブログの例では、チャットルームの接続通知に使う theme が依存配列に入っているため、テーマを変えるだけで再接続が起きていました。

import { useEffect, useEffectEvent } from 'react';

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]);
}

onConnected は呼ばれた時点の最新の theme を読みます。依存配列は roomId だけになり、テーマを変えても再接続しません。ただし公式ドキュメントは、依存配列の警告を黙らせる目的で使うことを禁じています。Effectから発火する「イベント」と呼べる処理にだけ使います。

ESLintが出す警告とエラーの文言

使い方の誤りは eslint-plugin-react-hooks が検出します。7.1.1(2026年4月17日公開)とESLint 10.11.0で、誤った使い方を2つ書いて実行した結果は次のとおりです。

  • 依存配列に入れた場合(exhaustive-deps の警告):Functions returned from `useEffectEvent` must not be included in the dependency array. Remove `onConnected` from the list
  • onClick から呼んだ場合(rules-of-hooks のエラー):`onConnected` is a function created with React Hook "useEffectEvent", and can only be called from Effects and Effect Events in the same component

ESLintを通さずにレンダー中に呼ぶと、実行時に A function wrapped in useEffectEvent can't be called during rendering. が投げられます(19.2.8・19.3.0とも同じ文言です)。返り値の関数はレンダーごとに作り直されるため、子コンポーネントへpropsで渡す使い方もできません。

memoとforwardRefの中では古い値を読む不具合

19.2系には、memo または forwardRef で包んだコンポーネントの useEffectEvent が、Contextの更新を反映しない不具合があります(GitHub issue #34818、修正はPR #34831)。次は検証コードの抜粋です。外部変数への代入と外部からの呼び出しは不具合観測用であり、通常の実装ではEffect内に呼び出しを閉じます。

const Ctx = createContext('POP');
let handler;

const Child = memo(function Child() {
  const nav = useContext(Ctx);
  const onTick = useEffectEvent(() => nav);
  useEffect(() => { handler = onTick; }, []);
  return <p>{nav}</p>;
});
// Providerの値を 'POP' から 'REPLACE' へ更新した後に handler() を呼ぶ

19.2.8では、画面は REPLACE を表示しているのに handler() は POP を返しました。memo を外した同じコンポーネントでは REPLACE を返します。19.3.0では、memo ありでも REPLACE でした。修正は2025年10月13日にマージされましたが、19.2.1以降のパッチには入っていません。19.2系のまま使うなら、memo の中ではContextの値を useEffectEvent 経由で読まない書き方にします。

cacheSignalの使い方:Server Componentsのキャッシュ終了時の処理中断

cacheSignal() は、cache() の寿命が終わったときにabortされる AbortSignal を返します。寿命が終わるのは、レンダーが完了したとき、中断されたとき、失敗したときです。fetch に渡しておくと、使われなくなったリクエストを止められます。

import { cache, cacheSignal } from 'react';

const dedupedFetch = cache(fetch);

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

使えるのはServer Componentsだけです。Client Componentsでは常に null が返り、レンダーの外で呼んだ場合も null です。null を返すのは、その範囲がキャッシュされていないことを示すためだと公式ドキュメントは説明しています。Server Componentsの境界と書き方はReact Server Components(RSC)の解説を参照してください。

Performance Tracksの見方:Chromeに追加されるSchedulerとComponents

19.2から、ChromeのDevToolsでPerformanceを記録すると、React専用のトラックが追加されます。Schedulerトラックは、Blocking(ユーザー操作などの同期更新)、Transition(startTransition の更新)、Suspense、Idleの4つのサブトラックに分かれます。どの優先度の作業がいつ描画されたかが見えます。Componentsトラックには、コンポーネントの描画とEffectの実行時間がフレームグラフで出ます。

表示されるのは開発ビルドとプロファイリングビルドだけで、本番ビルドでは計測のオーバーヘッドを避けるため無効です。本番相当で測るには、バンドラの設定で react-dom/client を react-dom/profiling に差し替えます。プロファイリングビルドの既定はSchedulerトラックだけです。Componentsトラックには <Profiler> で包んだ範囲だけが出ます(React Developer Toolsの拡張を入れていれば全コンポーネントが出ます)。

Partial Pre-renderingの手順:prerenderによる静的HTML生成とresumeによる描画再開

Partial Pre-renderingは、ページの静的な部分を先にHTMLとして作ってCDNから配り、動的な部分だけを後からサーバーで描画する仕組みです。19.2で、prerender が中断時の状態(postponed)を返すようになり、それを受け取って続きを描画する resume 系のAPIが加わりました。

import { prerender } from 'react-dom/static';
import { resume } from 'react-dom/server';

// ビルド時:AbortControllerで途中で止める
const controller = new AbortController();
const pending = prerender(<App />, { signal: controller.signal });
setTimeout(() => controller.abort(), 0);
const { prelude, postponed } = await pending;
await savePostponedState(JSON.stringify(postponed));
// prelude(静的HTML)をCDNへ

// リクエスト時:保存した状態から続きを描画
const state = JSON.parse(await getPostponedState(request));
const stream = await resume(<App />, state);

19.2.8で、<Suspense> の中に未解決のデータを読むコンポーネントを置いて試しました。結果は次のとおりです。

  • prelude はUTF-8で234バイト(文字列長は212)で、見出しと <Suspense> のfallback(「読み込み中…」)だけを含んでいました。
  • postponed はJSONに変換でき(624バイト)、resume に渡して描画できました。
  • resume の出力は、隠し要素に入った動的部分とfallbackを置き換えるscriptだけです。見出しなど prelude の部分は含まれません。
  • 中断せずに最後まで描画できた場合、postponed は null です。

resume の出力は、prelude を送った後に同じレスポンスへ続けて流す前提の断片です。単独で返してもページになりません。API名はWeb Streams用とNode Streams用で分かれています(resume/resumeToPipeableStream、resumeAndPrerender/resumeAndPrerenderToNodeStream)。Node.jsでは、公式は性能と圧縮対応の観点からNode Streams版を推奨しています。実際のHTTP圧縮は、サーバーやミドルウェアの設定も確認します。SSR・SSG・ISRの違いそのものはSSR・CSR・SSG・ISG・ISRの違いと使い分けで比較しています。

SSRとツールまわりの変更:Suspenseのバッチ表示・useId・ESLint

ストリーミングSSRでのSuspenseのバッチ表示

19.1までは、サーバーから届いたSuspenseの中身はすぐにfallbackと入れ替わっていました。19.2からは、少しの間待って複数の境界をまとめて表示します。クライアント描画と動きを揃えるための修正です。ページ読み込みが2.5秒(LCPの「良好」の基準)に近づくと、Reactはまとめるのをやめてすぐ表示します。上のPartial Pre-renderingの prelude の末尾に入っていた requestAnimationFrame のscriptは、この待ち時間の計測に使われます。

useIdの形式が _r_0_ に変わった影響

useId が返すIDを実際に出力すると、19.1.9は «r0»、19.2.8と19.3.0は _r_0_ でした。view-transition-name とXML 1.0の名前で使える形式にするための変更です。useId の値をスナップショットテストやE2Eテストのセレクタに直接書いている場合は、19.2に上げると一致しなくなります。テストでは getByRole やラベルで要素を取る書き方に置き換えます。

eslint-plugin-react-hooksの版別設定:6.xと7.xの推奨プリセット

19.2と同日の2025年10月1日に出た eslint-plugin-react-hooks 6.1.0で、recommended がflat config向けになりました。6.1.0には設定形式の不具合があり、6.1.1(2025年10月3日)で直っています。6.xで旧形式(.eslintrc)を使い続ける場合は plugin:react-hooks/recommended-legacy に書き換え、React Compiler関連ルールは任意で有効にします。7.0.0(2025年10月8日)以降は recommended-legacy が無くなり、推奨設定にCompiler関連ルールも含まれます。flat configは configs.flat.recommended、旧形式は plugin:react-hooks/recommended を使います。Compilerを使うかどうかの判断はReact Compilerとは?自動メモ化の仕組みと導入方法で扱っています。

19.2.1〜19.2.8の修正内容:RSCの脆弱性対策・不具合修正・性能改善

版 npm公開日 内容
19.2.1 2025-12-03 CVE-2025-55182(RCE)の修正
19.2.2 2025-12-11 CVE-2025-55184(DoS)・CVE-2025-55183(ソース露出)
19.2.3 2025-12-11 CVE-2025-67779(DoS・後に不完全と判明)
19.2.4 2026-01-26 DoS修正の完全版(CVE-2026-23864)
19.2.5 2026-04-08 循環参照の保護を追加
19.2.6 2026-05-06 型の強化と性能改善
19.2.7 2026-06-01 19.2.6のFormData欠落を修正
19.2.8 2026-07-21 デコード時の性能改善

8本ともGitHubのリリースノートは「React Server Components」の項目だけで、Activity や useEffectEvent の修正は1つもありません。脆弱性の対象は react-server-dom-webpack・react-server-dom-parcel・react-server-dom-turbopack で、サーバーを使わないReactアプリは影響を受けません。React公式は、19.2.3までのパッチではDoS対策が不完全だったとして再更新を求めました。RSCを使う構成の下限は19.2.4です。19.2.6でServer ActionsのFormDataが欠ける不具合が入り、19.2.7で直っているため、19.2系に留まるなら19.2.8まで上げます。脆弱性の経緯と影響範囲はReact Server Componentsの脆弱性CVE-2025-55182とはにまとめています。

19.2から19.3へ上げる判断:19.2系には戻されない修正

19.3.0(2026年9月9日)には、19.2で入った機能の不具合修正がまとまって入っています。どれも19.2系のパッチには入っていません。

  • forwardRef・memo の中の useEffectEvent が最新の値を読むように修正
  • 隠した <Activity> の中のポータルの中身も隠すように変更
  • 隠している間に起きたストアの変更を useSyncExternalStore が取りこぼす不具合の修正
  • 隠した <Activity> 内のエラーが表示中のUIへ伝わらないように修正
  • hidden に切り替えた後も <title> が <head> に残る不具合の修正

<Activity> か useEffectEvent を本番で使っているなら、19.3へ上げるべきです。19.2系のまま回避策を重ねるより安全です。どちらも使っておらず、RSCのパッチだけを追いたい場合は、利用中のフレームワークの対応版と既知の不具合を確認したうえで、19.2.8に留まる判断もできます。19.3では <ViewTransition> とFragment refsが安定版になり、互いに無関係なTransitionが別々に描画されるようになりました。このため、Transitionの完了順に依存した処理は上げた後に確認が要ります。19.3で増えたAPIの動くコードと、更新時に挙動が変わる箇所の一覧はReact 19.3の新機能|ViewTransition・Fragment ref・browser()の実装と19.2からの更新手順にまとめています。19.3の非同期まわりの書き方はAsync Reactとは?Transition・Suspense・useOptimisticで非同期UIを組む実装【React 19.3】で、19.3の機能全体は冒頭のReact 19の解説記事で扱っています。

よくある質問

React 19.2に破壊的変更はありますか?

公開APIの削除はありません。影響が出やすいのは、useId の出力が «r0» から _r_0_ に変わった点と、eslint-plugin-react-hooks の recommended がflat config向けになった点です。前者はIDを文字列で照合するテストが、後者は .eslintrc の設定が対象です。

React 19.1と19.2の違いは何ですか?

19.2で <Activity>・useEffectEvent・cacheSignal・Performance Tracks・Partial Pre-renderingの resume 系APIが加わりました。19.1.9にはこれらがexportされておらず、useEffectEvent を呼ぶとTypeErrorになります。19.1.0(2025年3月28日)の主な追加はOwner Stacksです。

useEffectEventでESLintの警告が出るのはなぜですか?

返り値を依存配列に入れると exhaustive-deps が警告し、Effect以外(イベントハンドラーやレンダー中)から呼ぶと rules-of-hooks がエラーにします。どちらも eslint-plugin-react-hooks 6.1.0以降が useEffectEvent を認識して出すものです。古いプラグインのままだと逆に依存配列へ追加するよう促されるため、プラグインを先に上げます。

React 19.3では何が変わりましたか?

<ViewTransition> とFragment refsが安定版になり、react-dom に browser() が、DOMの書き込みにTrusted Typesへの対応が入りました。19.2の useEffectEvent と <Activity> の不具合修正も19.3に含まれます。

React 20はいつ出ますか?

2026年10月3日時点で、npmの react に20.0.0は公開されていません。React公式も公開日を発表していません。latest タグは19.3.0、canary タグは19.3.0系の開発版を指しています。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.07.21 テックブログ Apache Tomcatの脆弱性一覧【2026年10月】最新版11.0.27・10.1.61・9.0.123とCVE-2026-59083ほかの影響確認・対応手順
  2. 2026.10.21 テックブログ NEC委託先の不正アクセスとは|顧客システム暗号化・情報漏えいの経緯と委託元が点検する正規アカウント
  3. 2026.10.21 テックブログ ふるさとチョイス不正アクセスで445万件流出:対象確認・パスワード変更と再開時期
  4. 2026.10.22 テックブログ Tomcatバージョン一覧とサポート期限【2026年10月】9.0系は2027年3月末で終了・9.1系と11.0系への移行判断
  5. 2024.07.05 コラム XAMPPの脆弱性とは?危険性とCVE-2024-4577への対策をわかりやすく解説【2026年10月】

RELATED POSTS 関連記事

目次