react-19

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React 19 - Key Changes

React 19 - 核心变更

This skill focuses on what changed in React 19. Not a complete React reference.
本内容聚焦于React 19中的变更点,并非完整的React参考手册。

Coming from React 16/17?

从React 16/17升级?

If upgrading from pre-18 versions, these changes accumulated and are now mandatory:
ChangeIntroducedReact 19 Status
createRoot
/
hydrateRoot
React 18Required (
ReactDOM.render
removed)
Concurrent renderingReact 18Foundation for all R19 features
Automatic batchingReact 18Default behavior
useId
,
useSyncExternalStore
React 18Stable, commonly used
Hooks (no classes for new code)React 16.8Only path for new features
createContext
(not legacy)
React 16.3Required (legacy Context removed)
Error BoundariesReact 16Now with better error callbacks
Migration path: Upgrade to React 18.3 first (shows deprecation warnings), then to 19.
如果从18之前的版本升级,以下累积的变更现在是强制要求
变更内容引入版本React 19 状态
createRoot
/
hydrateRoot
React 18必须使用
ReactDOM.render
已移除)
并发渲染React 18所有R19功能的基础
自动批处理React 18默认行为
useId
,
useSyncExternalStore
React 18稳定版,广泛使用
钩子(新代码不再使用类组件)React 16.8新功能的唯一实现方式
createContext
(非遗留版本)
React 16.3必须使用(遗留Context已移除)
错误边界React 16现在支持更完善的错误回调
迁移路径: 先升级到React 18.3(该版本会显示废弃警告),再升级到19。

The React 19 Mindset

React 19 的思维转变

React 19 represents fundamental shifts in how to think about React:
Old ThinkingNew Thinking
Client-side by defaultServer-first (RSC default)
Manual memoizationCompiler handles it
useEffect
for data
async Server Components
useState
for forms
Form Actions
Loading state booleansSuspense boundaries
Optimize everythingWrite correct code, compiler optimizes
See references/paradigm-shifts.md for the mental model changes.
See references/anti-patterns.md for what to stop doing.
React 19代表了React开发思路的根本性转变:
旧思维新思维
默认客户端优先服务端优先(RSC默认模式)
手动记忆化编译器自动处理
useEffect
处理数据
异步服务端组件
useState
处理表单
Form Actions
用布尔值管理加载状态Suspense边界
优化所有内容先编写正确的代码,编译器负责优化
查看 references/paradigm-shifts.md 了解思维模型的具体变化。
查看 references/anti-patterns.md 了解需要摒弃的过时模式。

Quick Reference: What's New

快速参考:新增内容

FeatureReact 18React 19+
MemoizationManual (
useMemo
,
useCallback
,
memo
)
React Compiler (automatic) or manual
Forward refs
forwardRef()
wrapper
ref
as regular prop
Context provider
<Context.Provider value={}>
<Context value={}>
Form stateCustom with
useState
useActionState
hook
Optimistic updatesManual state management
useOptimistic
hook
Read promisesNot possible in render
use()
hook
Conditional contextNot possible
use(Context)
after conditionals
Form pending stateManual tracking
useFormStatus
hook
Ref cleanupPass
null
on unmount
Return cleanup function
Document metadata
react-helmet
or manual
Native
<title>
,
<meta>
,
<link>
Hide/show UI with stateUnmount/remount (state lost)
<Activity>
component (19.2+)
Non-reactive Effect logicAdd to deps or suppress lint
useEffectEvent
hook (19.2+)
Custom ElementsPartial supportFull support (props as properties)
Hydration errorsMultiple vague errorsSingle error with diff
特性React 18React 19+
记忆化手动实现(
useMemo
,
useCallback
,
memo
React Compiler自动处理或手动实现
转发ref
forwardRef()
包装器
ref
作为常规属性
Context提供者
<Context.Provider value={}>
<Context value={}>
表单状态
useState
自定义实现
useActionState
钩子
乐观更新手动状态管理
useOptimistic
钩子
读取Promise渲染中无法实现
use()
钩子
条件式Context无法实现可在条件判断后使用
use(Context)
表单 pending 状态手动跟踪
useFormStatus
钩子
Ref清理在卸载时传入
null
返回清理函数
文档元数据
react-helmet
或手动实现
原生支持
<title>
,
<meta>
,
<link>
用状态控制UI显示/隐藏卸载/重新挂载(状态丢失)
<Activity>
组件(19.2+)
非响应式Effect逻辑添加依赖或抑制lint提示
useEffectEvent
钩子(19.2+)
自定义元素部分支持完整支持(属性作为对象属性传递)
水合错误多个模糊错误单个带差异对比的错误

React Compiler & Memoization

React Compiler 与记忆化

With React Compiler enabled, manual memoization is optional, not forbidden:
tsx
// React Compiler handles this automatically
function Component({ items }) {
  const filtered = items.filter(x => x.active);
  const sorted = filtered.sort((a, b) => a.name.localeCompare(b.name));
  const handleClick = (id) => console.log(id);
  return <List items={sorted} onClick={handleClick} />;
}

// Manual memoization still works as escape hatch for fine-grained control
const filtered = useMemo(() => expensiveOperation(items), [items]);
const handleClick = useCallback((id) => onClick(id), [onClick]);
When to use manual memoization with React Compiler:
  • Effect dependencies that need stable references
  • Sharing expensive calculations across components (compiler doesn't share)
  • Explicit control over when re-computation happens
See references/react-compiler.md for details.
启用React Compiler后,手动记忆化是可选操作,而非被禁止
tsx
// React Compiler会自动处理以下代码
function Component({ items }) {
  const filtered = items.filter(x => x.active);
  const sorted = filtered.sort((a, b) => a.name.localeCompare(b.name));
  const handleClick = (id) => console.log(id);
  return <List items={sorted} onClick={handleClick} />;
}

// 手动记忆化仍可作为细粒度控制的兜底方案
const filtered = useMemo(() => expensiveOperation(items), [items]);
const handleClick = useCallback((id) => onClick(id), [onClick]);
启用React Compiler后仍需手动记忆化的场景:
  • 需要稳定引用的Effect依赖项
  • 组件间共享的昂贵计算(编译器不会共享计算结果)
  • 需要显式控制重新计算时机的场景
查看 references/react-compiler.md 获取详细信息。

ref as Prop (forwardRef Deprecated)

ref 作为属性(forwardRef 已废弃)

tsx
// React 19: ref is just a prop
function Input({ placeholder, ref }) {
  return <input placeholder={placeholder} ref={ref} />;
}

// Usage - no change
const inputRef = useRef(null);
<Input ref={inputRef} placeholder="Enter text" />

// forwardRef still works but will be deprecated
// Codemod: npx codemod@latest react/19/replace-forward-ref
tsx
// React 19: ref 只是一个普通属性
function Input({ placeholder, ref }) {
  return <input placeholder={placeholder} ref={ref} />;
}

// 使用方式无变化
const inputRef = useRef(null);
<Input ref={inputRef} placeholder="Enter text" />

// forwardRef 仍可使用但会被废弃
// 代码迁移工具:npx codemod@latest react/19/replace-forward-ref

Ref Cleanup Functions

Ref 清理函数

tsx
// React 19: Return cleanup function from ref callback
<input
  ref={(node) => {
    // Setup
    node?.focus();
    // Return cleanup (called on unmount or ref change)
    return () => {
      console.log('Cleanup');
    };
  }}
/>

// React 18: Received null on unmount (still works, but cleanup preferred)
<input ref={(node) => {
  if (node) { /* setup */ }
  else { /* cleanup */ }
}} />
tsx
// React 19: 从ref回调中返回清理函数
<input
  ref={(node) => {
    // 初始化操作
    node?.focus();
    // 返回清理函数(在组件卸载或ref变更时调用)
    return () => {
      console.log('Cleanup');
    };
  }}
/>

// React 18: 在卸载时传入null(仍可工作,但推荐使用清理函数)
<input ref={(node) => {
  if (node) { /* 初始化 */ }
  else { /* 清理 */ }
}} />

Context as Provider

Context 直接作为提供者

tsx
const ThemeContext = createContext('light');

// React 19: Use Context directly
function App({ children }) {
  return (
    <ThemeContext value="dark">
      {children}
    </ThemeContext>
  );
}

// React 18: Required .Provider (still works, will be deprecated)
<ThemeContext.Provider value="dark">
  {children}
</ThemeContext.Provider>
tsx
const ThemeContext = createContext('light');

// React 19: 直接使用Context
function App({ children }) {
  return (
    <ThemeContext value="dark">
      {children}
    </ThemeContext>
  );
}

// React 18: 必须使用.Provider(仍可工作,但会被废弃)
<ThemeContext.Provider value="dark">
  {children}
</ThemeContext.Provider>

New Hooks

新钩子

useActionState

useActionState

tsx
import { useActionState } from 'react';

function Form() {
  const [error, submitAction, isPending] = useActionState(
    async (prevState, formData) => {
      const result = await saveData(formData.get('name'));
      if (result.error) return result.error;
      redirect('/success');
      return null;
    },
    null // initial state
  );

  return (
    <form action={submitAction}>
      <input name="name" disabled={isPending} />
      <button disabled={isPending}>
        {isPending ? 'Saving...' : 'Save'}
      </button>
      {error && <p className="error">{error}</p>}
    </form>
  );
}
tsx
import { useActionState } from 'react';

function Form() {
  const [error, submitAction, isPending] = useActionState(
    async (prevState, formData) => {
      const result = await saveData(formData.get('name'));
      if (result.error) return result.error;
      redirect('/success');
      return null;
    },
    null // 初始状态
  );

  return (
    <form action={submitAction}>
      <input name="name" disabled={isPending} />
      <button disabled={isPending}>
        {isPending ? 'Saving...' : 'Save'}
      </button>
      {error && <p className="error">{error}</p>}
    </form>
  );
}

useOptimistic

useOptimistic

tsx
import { useOptimistic } from 'react';

function Messages({ messages, sendMessage }) {
  const [optimisticMessages, addOptimistic] = useOptimistic(
    messages,
    (state, newMessage) => [...state, { ...newMessage, sending: true }]
  );

  async function handleSubmit(formData) {
    const message = { text: formData.get('text'), id: Date.now() };
    addOptimistic(message); // Show immediately
    await sendMessage(message); // Reverts on error
  }

  return (
    <form action={handleSubmit}>
      {optimisticMessages.map(m => (
        <div key={m.id} style={{ opacity: m.sending ? 0.5 : 1 }}>
          {m.text}
        </div>
      ))}
      <input name="text" />
    </form>
  );
}
tsx
import { useOptimistic } from 'react';

function Messages({ messages, sendMessage }) {
  const [optimisticMessages, addOptimistic] = useOptimistic(
    messages,
    (state, newMessage) => [...state, { ...newMessage, sending: true }]
  );

  async function handleSubmit(formData) {
    const message = { text: formData.get('text'), id: Date.now() };
    addOptimistic(message); // 立即显示
    await sendMessage(message); // 出错时回滚
  }

  return (
    <form action={handleSubmit}>
      {optimisticMessages.map(m => (
        <div key={m.id} style={{ opacity: m.sending ? 0.5 : 1 }}>
          {m.text}
        </div>
      ))}
      <input name="text" />
    </form>
  );
}

use() Hook

use() Hook

tsx
import { use, Suspense } from 'react';

// Read promises (suspends until resolved)
function Comments({ commentsPromise }) {
  const comments = use(commentsPromise);
  return comments.map(c => <p key={c.id}>{c.text}</p>);
}

// Usage with Suspense
<Suspense fallback={<Spinner />}>
  <Comments commentsPromise={fetchComments()} />
</Suspense>

// Conditional context reading (not possible with useContext!)
function Theme({ showTheme }) {
  if (!showTheme) return <div>Plain</div>;

  const theme = use(ThemeContext); // Can be called conditionally!
  return <div style={{ color: theme.primary }}>Themed</div>;
}
tsx
import { use, Suspense } from 'react';

// 读取Promise(会挂起直到解析完成)
function Comments({ commentsPromise }) {
  const comments = use(commentsPromise);
  return comments.map(c => <p key={c.id}>{c.text}</p>);
}

// 结合Suspense使用
<Suspense fallback={<Spinner />}>
  <Comments commentsPromise={fetchComments()} />
</Suspense>

// 条件式读取Context(useContext无法实现!)
function Theme({ showTheme }) {
  if (!showTheme) return <div>Plain</div>;

  const theme = use(ThemeContext); // 可在条件判断后调用!
  return <div style={{ color: theme.primary }}>Themed</div>;
}

useFormStatus (react-dom)

useFormStatus (react-dom)

tsx
import { useFormStatus } from 'react-dom';

// Must be used inside a <form> - reads parent form status
function SubmitButton() {
  const { pending, data, method, action } = useFormStatus();
  return (
    <button disabled={pending}>
      {pending ? 'Submitting...' : 'Submit'}
    </button>
  );
}

function Form() {
  return (
    <form action={serverAction}>
      <input name="email" />
      <SubmitButton /> {/* Reads form status via context */}
    </form>
  );
}
See references/new-hooks.md for complete API details.
tsx
import { useFormStatus } from 'react-dom';

// 必须在<form>内部使用 - 读取父表单的状态
function SubmitButton() {
  const { pending, data, method, action } = useFormStatus();
  return (
    <button disabled={pending}>
      {pending ? 'Submitting...' : 'Submit'}
    </button>
  );
}

function Form() {
  return (
    <form action={serverAction}>
      <input name="email" />
      <SubmitButton /> {/* 通过Context读取表单状态 */}
    </form>
  );
}
查看 references/new-hooks.md 获取完整API详情。

Form Actions

Form Actions

tsx
// Pass function directly to form action
<form action={async (formData) => {
  'use server';
  await saveToDatabase(formData);
}}>
  <input name="email" type="email" />
  <button type="submit">Subscribe</button>
</form>

// Button-level actions
<form>
  <button formAction={saveAction}>Save</button>
  <button formAction={deleteAction}>Delete</button>
</form>

// Manual form reset
import { requestFormReset } from 'react-dom';
requestFormReset(formElement);
tsx
// 直接将函数传递给form的action属性
<form action={async (formData) => {
  'use server';
  await saveToDatabase(formData);
}}>
  <input name="email" type="email" />
  <button type="submit">Subscribe</button>
</form>

// 按钮级别的action
<form>
  <button formAction={saveAction}>Save</button>
  <button formAction={deleteAction}>Delete</button>
</form>

// 手动重置表单
import { requestFormReset } from 'react-dom';
requestFormReset(formElement);

Document Metadata

文档元数据

tsx
// Automatically hoisted to <head> - works in any component
function BlogPost({ post }) {
  return (
    <article>
      <title>{post.title}</title>
      <meta name="description" content={post.excerpt} />
      <meta name="author" content={post.author} />
      <link rel="canonical" href={post.url} />
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}
tsx
// 会自动提升到<head> - 可在任意组件中使用
function BlogPost({ post }) {
  return (
    <article>
      <title>{post.title}</title>
      <meta name="description" content={post.excerpt} />
      <meta name="author" content={post.author} />
      <link rel="canonical" href={post.url} />
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

Resource Preloading

资源预加载

tsx
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';

function App() {
  // DNS prefetch
  prefetchDNS('https://api.example.com');

  // Establish connection early
  preconnect('https://fonts.googleapis.com');

  // Preload resources
  preload('https://example.com/font.woff2', { as: 'font' });
  preload('/hero.jpg', { as: 'image' });

  // Load and execute script eagerly
  preinit('https://example.com/analytics.js', { as: 'script' });

  return <main>...</main>;
}
tsx
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';

function App() {
  // DNS 预获取
  prefetchDNS('https://api.example.com');

  // 提前建立连接
  preconnect('https://fonts.googleapis.com');

  // 预加载资源
  preload('https://example.com/font.woff2', { as: 'font' });
  preload('/hero.jpg', { as: 'image' });

  // 提前加载并执行脚本
  preinit('https://example.com/analytics.js', { as: 'script' });

  return <main>...</main>;
}

Stylesheet Support

样式表支持

tsx
// precedence controls insertion order and deduplication
function Component() {
  return (
    <>
      <link rel="stylesheet" href="/base.css" precedence="default" />
      <link rel="stylesheet" href="/theme.css" precedence="high" />
      <div className="styled">Content</div>
    </>
  );
}

// React ensures stylesheets load before Suspense boundary reveals
<Suspense fallback={<Skeleton />}>
  <link rel="stylesheet" href="/feature.css" precedence="default" />
  <FeatureComponent />
</Suspense>
tsx
// precedence 控制插入顺序和去重
function Component() {
  return (
    <>
      <link rel="stylesheet" href="/base.css" precedence="default" />
      <link rel="stylesheet" href="/theme.css" precedence="high" />
      <div className="styled">Content</div>
    </>
  );
}

// React 确保样式表在Suspense边界显示前加载完成
<Suspense fallback={<Skeleton />}>
  <link rel="stylesheet" href="/feature.css" precedence="default" />
  <FeatureComponent />
</Suspense>

Custom Elements Support

自定义元素支持

React 19 adds full support for Custom Elements (Web Components).
tsx
// Props matching element properties are assigned as properties
// Others are assigned as attributes
<my-element
  stringAttr="hello"           // Attribute (string)
  complexProp={{ foo: 'bar' }} // Property (object)
  onCustomEvent={handleEvent}  // Property (function)
/>
Client-side: React checks if a property exists on the element instance. If yes, assigns as property; otherwise, as attribute.
Server-side (SSR): Primitive types (string, number) render as attributes. Objects, functions, symbols are omitted from HTML.
tsx
// Define custom element
class MyElement extends HTMLElement {
  constructor() {
    super();
    this.data = undefined; // React will assign to this property
  }

  connectedCallback() {
    this.textContent = JSON.stringify(this.data);
  }
}
customElements.define('my-element', MyElement);

// Use in React
<my-element data={{ items: [1, 2, 3] }} />
React 19 添加了对自定义元素(Web Components)的完整支持。
tsx
// 匹配元素属性的props会作为对象属性赋值
// 其他props会作为HTML属性赋值
<my-element
  stringAttr="hello"           // HTML属性(字符串类型)
  complexProp={{ foo: 'bar' }} // 对象属性(对象类型)
  onCustomEvent={handleEvent}  // 对象属性(函数类型)
/>
客户端: React会检查元素实例是否存在对应的属性。如果存在,则作为对象属性赋值;否则作为HTML属性赋值。
服务端(SSR): 原始类型(字符串、数字)会渲染为HTML属性。对象、函数、符号类型会从HTML中省略。
tsx
// 定义自定义元素
class MyElement extends HTMLElement {
  constructor() {
    super();
    this.data = undefined; // React会将值赋值给该属性
  }

  connectedCallback() {
    this.textContent = JSON.stringify(this.data);
  }
}
customElements.define('my-element', MyElement);

// 在React中使用
<my-element data={{ items: [1, 2, 3] }} />

Hydration Improvements

水合优化

Better Error Messages

更友好的错误提示

React 19 shows a single error with a diff instead of multiple vague errors:
Uncaught Error: Hydration failed because the server rendered HTML
didn't match the client.

<App>
  <span>
+   Client
-   Server
React 19会显示单个带差异对比的错误,而非多个模糊错误:
Uncaught Error: Hydration failed because the server rendered HTML
didn't match the client.

<App>
  <span>
+   Client
-   Server

Third-Party Script Compatibility

第三方脚本兼容性

React 19 gracefully handles elements inserted by browser extensions or third-party scripts:
  • Unexpected tags in
    <head>
    and
    <body>
    are skipped (no mismatch errors)
  • Stylesheets from extensions are preserved during re-renders
  • No need to add
    suppressHydrationWarning
    for extension-injected content
React 19可优雅处理浏览器扩展或第三方脚本插入的元素:
  • 忽略
    <head>
    <body>
    中的意外标签(不会触发不匹配错误)
  • 保留扩展注入的样式表在重新渲染时不丢失
  • 无需为扩展注入的内容添加
    suppressHydrationWarning

Removed APIs (Breaking)

已移除的API(重大变更)

RemovedMigration
ReactDOM.render()
createRoot().render()
ReactDOM.hydrate()
hydrateRoot()
unmountComponentAtNode()
root.unmount()
ReactDOM.findDOMNode()
Use refs
propTypes
TypeScript or remove
defaultProps
(functions)
ES6 default parameters
String refsCallback refs or
useRef
Legacy Context
createContext
React.createFactory
JSX
react-dom/test-utils
act
from
'react'
See references/deprecations.md for migration guides.
已移除API迁移方案
ReactDOM.render()
createRoot().render()
ReactDOM.hydrate()
hydrateRoot()
unmountComponentAtNode()
root.unmount()
ReactDOM.findDOMNode()
使用refs替代
propTypes
使用TypeScript或移除
defaultProps
(函数组件)
ES6默认参数
字符串refs回调refs或
useRef
遗留Context
createContext
React.createFactory
JSX
react-dom/test-utils
'react'
导入
act
查看 references/deprecations.md 获取迁移指南。

TypeScript Changes

TypeScript 变更

tsx
// useRef requires argument
const ref = useRef<HTMLDivElement>(null); // Required
const ref = useRef(); // Error in React 19

// Ref callbacks must not return values (except cleanup)
<div ref={(node) => { instance = node; }} /> // Correct
<div ref={(node) => (instance = node)} />    // Error - implicit return

// ReactElement props are now unknown (not any)
type Props = ReactElement['props']; // unknown in R19, any in R18

// JSX namespace - import explicitly
import type { JSX } from 'react';
See references/typescript-changes.md for codemods.
tsx
// useRef 必须传入参数
const ref = useRef<HTMLDivElement>(null); // 必须传入
const ref = useRef(); // React 19中会报错

// Ref回调不能返回值(清理函数除外)
<div ref={(node) => { instance = node; }} /> // 正确
<div ref={(node) => (instance = node)} />    // 错误 - 隐式返回

// ReactElement props 类型现在为unknown(之前为any)
type Props = ReactElement['props']; // R19中为unknown,R18中为any

// JSX 命名空间 - 需要显式导入
import type { JSX } from 'react';
查看 references/typescript-changes.md 获取代码迁移工具。

Migration Codemods

迁移代码工具

bash
undefined
bash
undefined

Run all React 19 codemods

运行所有React 19迁移工具

npx codemod@latest react/19/migration-recipe
npx codemod@latest react/19/migration-recipe

Individual codemods

单个迁移工具

npx codemod@latest react/19/replace-reactdom-render npx codemod@latest react/19/replace-string-ref npx codemod@latest react/19/replace-act-import npx codemod@latest react/19/replace-use-form-state npx codemod@latest react/prop-types-typescript
npx codemod@latest react/19/replace-reactdom-render npx codemod@latest react/19/replace-string-ref npx codemod@latest react/19/replace-act-import npx codemod@latest react/19/replace-use-form-state npx codemod@latest react/prop-types-typescript

TypeScript types

TypeScript 类型迁移

npx types-react-codemod@latest preset-19 ./src
undefined
npx types-react-codemod@latest preset-19 ./src
undefined

Imports (Best Practice)

导入方式(最佳实践)

tsx
// Named imports (recommended)
import { useState, useEffect, useRef, use } from 'react';
import { createRoot } from 'react-dom/client';
import { useFormStatus } from 'react-dom';

// Default import still works but named preferred
import React from 'react'; // Works but not recommended
tsx
// 命名导入(推荐)
import { useState, useEffect, useRef, use } from 'react';
import { createRoot } from 'react-dom/client';
import { useFormStatus } from 'react-dom';

// 默认导入仍可工作,但推荐使用命名导入
import React from 'react'; // 可工作但不推荐

Error Handling Changes

错误处理变更

tsx
// React 19 error handling options
const root = createRoot(container, {
  onUncaughtError: (error, errorInfo) => {
    // Errors not caught by Error Boundary
    console.error('Uncaught:', error, errorInfo.componentStack);
  },
  onCaughtError: (error, errorInfo) => {
    // Errors caught by Error Boundary
    reportToAnalytics(error);
  },
  onRecoverableError: (error, errorInfo) => {
    // Errors React recovered from automatically
    console.warn('Recovered:', error);
  }
});
See references/suspense-streaming.md for Suspense patterns and error boundaries.
tsx
// React 19 错误处理选项
const root = createRoot(container, {
  onUncaughtError: (error, errorInfo) => {
    // 未被Error Boundary捕获的错误
    console.error('Uncaught:', error, errorInfo.componentStack);
  },
  onCaughtError: (error, errorInfo) => {
    // 被Error Boundary捕获的错误
    reportToAnalytics(error);
  },
  onRecoverableError: (error, errorInfo) => {
    // React自动恢复的错误
    console.warn('Recovered:', error);
  }
});
查看 references/suspense-streaming.md 获取Suspense模式和错误边界相关内容。

React 19.2+ Features

React 19.2+ 新特性

Activity Component (19.2)

Activity 组件(19.2)

Hide/show UI while preserving state (like background tabs):
tsx
import { Activity } from 'react';

// State preserved when hidden, Effects cleaned up
<Activity mode={isVisible ? 'visible' : 'hidden'}>
  <ExpensiveComponent />
</Activity>
隐藏/显示UI的同时保留状态(类似后台标签页的行为):
tsx
import { Activity } from 'react';

// 隐藏时保留状态,Effect会被清理
<Activity mode={isVisible ? 'visible' : 'hidden'}>
  <ExpensiveComponent />
</Activity>

useEffectEvent Hook (19.2)

useEffectEvent 钩子(19.2)

Extract non-reactive logic from Effects without adding dependencies:
tsx
import { useEffect, useEffectEvent } from 'react';

function Chat({ roomId, theme }) {
  // Reads theme without making it a dependency
  const onConnected = useEffectEvent(() => {
    showNotification(`Connected!`, theme);
  });

  useEffect(() => {
    const conn = connect(roomId);
    conn.on('connected', onConnected);
    return () => conn.disconnect();
  }, [roomId]); // theme NOT in deps - won't reconnect on theme change
}
See references/react-19-2-features.md for complete 19.1+ and 19.2 features.
从Effect中提取非响应式逻辑,无需添加依赖项:
tsx
import { useEffect, useEffectEvent } from 'react';

function Chat({ roomId, theme }) {
  // 读取theme但无需将其作为依赖项
  const onConnected = useEffectEvent(() => {
    showNotification(`Connected!`, theme);
  });

  useEffect(() => {
    const conn = connect(roomId);
    conn.on('connected', onConnected);
    return () => conn.disconnect();
  }, [roomId]); // theme不在依赖项中 - 主题变更时不会重新连接
}
查看 references/react-19-2-features.md 获取19.1+和19.2的完整特性。

Reference Documentation

参考文档

DocumentContent
paradigm-shifts.mdMental model changes - how to think in React 19
anti-patterns.mdWhat to stop doing - outdated patterns
react-19-2-features.mdReact 19.1+ and 19.2 features (Activity, useEffectEvent)
new-hooks.mdComplete API for 19.0 hooks
server-components.mdRSC, Server Actions, directives
suspense-streaming.mdSuspense, streaming, error handling
react-compiler.mdAutomatic memoization details
deprecations.mdRemoved APIs with migration guides
typescript-changes.mdType changes and codemods
文档内容
paradigm-shifts.md思维模型变更 - React 19的思考方式
anti-patterns.md需要摒弃的过时模式
react-19-2-features.mdReact 19.1+和19.2特性(Activity, useEffectEvent)
new-hooks.md19.0新钩子的完整API
server-components.mdRSC、Server Actions、指令
suspense-streaming.mdSuspense、流式渲染、错误处理
react-compiler.md自动记忆化详情
deprecations.md已移除API及迁移指南
typescript-changes.md类型变更及代码迁移工具