React 19 新特性完全指南:Actions、use() Hook、服务器组件、编译器与自动记忆化

React 19 深度解析:Actions 异步状态管理、use() Hook 统一资源消费模式、React Compiler(React Forget)自动记忆化、Server Components 稳定版、Document Metadata API、Asset Loading、表单增强与新 Hooks。包含升级路径与兼容性评估。

React 19 是 React 自 Hooks 以来最重要的版本更新。它不仅让 Server Components 脱离实验阶段,还引入了 Actions、use() Hook 和备受期待的 React Compiler(前身为 React Forget),将从根本上改变 React 的性能优化范式——开发者不再需要手动写 useMemouseCallbackReact.memo


一、React 19 核心变化速览

特性状态影响本文章节
Actions稳定异步状态管理原生化
use() Hook稳定Promise、Context、Suspense 统一消费
React CompilerBeta自动记忆化,消除手动优化
Server Components稳定服务端渲染原生支持
Document Metadata稳定<title><meta> 内置支持
Asset Loading稳定样式/字体预加载集成
Form Actions稳定HTML Form + React 深度整合
新 Ref API稳定ref 作为 Prop 传递
Context 作为 Provider稳定<Context> 直接当 Provider

二、Actions:原生的异步状态管理

2.1 为什么需要 Actions?

在 React 19 之前,异步操作(提交表单、调用 API)的状态管理需要自己实现:

// ❌ React 18 的样板代码
function Form() {
  const [isPending, setIsPending] = useState(false);
  const [error, setError] = useState(null);

  const handleSubmit = async (formData) => {
    setIsPending(true);
    setError(null);
    try {
      await submitForm(formData);
    } catch (e) {
      setError(e);
    } finally {
      setIsPending(false);
    }
  };
}

Actions 将这个模式内置到 React 中。

2.2 useTransition + Action 基础模式

import { useTransition } from 'react';

function UpdateName() {
  const [isPending, startTransition] = useTransition();

  const handleSubmit = async (formData) => {
    startTransition(async () => {
      await updateName(formData.get('name'));
    });
  };

  return (
    <form action={handleSubmit}>
      <input name="name" />
      <button type="submit" disabled={isPending}>
        {isPending ? 'Updating...' : 'Update'}
      </button>
    </form>
  );
}

核心变化

  • action 属性接收 async 函数
  • isPending 自动跟踪异步状态
  • Transition 包裹保证 UI 不被阻塞

2.3 useActionState(原 useFormState)

import { useActionState } from 'react';

async function submitAction(prevState, formData) {
  const name = formData.get('name');

  try {
    await updateName(name);
    return { success: true, message: 'Name updated!' };
  } catch (error) {
    return { success: false, message: error.message };
  }
}

function Form() {
  const [state, formAction, isPending] = useActionState(submitAction, {
    success: false,
    message: '',
  });

  return (
    <form action={formAction}>
      <input name="name" required />
      <button type="submit" disabled={isPending}>
        {isPending ? 'Updating...' : 'Update'}
      </button>
      {state.message && (
        <p className={state.success ? 'success' : 'error'}>
          {state.message}
        </p>
      )}
    </form>
  );
}

useActionState 三返回

  • state:Action 返回的最新状态
  • formAction:绑定到 form 的 action handler
  • isPending:Action 是否执行中

2.4 useOptimistic:乐观更新

import { useOptimistic } from 'react';

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

  const sendMessage = async (formData) => {
    const message = formData.get('message');

    // 立即显示乐观消息
    addOptimisticMessage({ text: message });

    // 实际发送
    await api.sendMessage(message);
  };

  return (
    <div>
      {optimisticMessages.map((msg, i) => (
        <div key={i} style={{ opacity: msg.sending ? 0.5 : 1 }}>
          {msg.text}
        </div>
      ))}
      <form action={sendMessage}>
        <input name="message" />
        <button>Send</button>
      </form>
    </div>
  );
}

乐观更新流程

  1. 用户提交 → UI 立即更新(乐观状态)
  2. 实际请求发送
  3. 请求成功 → 乐观状态与实际状态合并
  4. 请求失败 → 乐观状态回滚,显示错误

2.5 Actions vs TanStack Query / RTK Query

维度ActionsTanStack Query
定位React 原生异步模式独立数据获取库
缓存无内置缓存✅ 智能缓存
重取手动✅ 自动重取、窗口聚焦重取
乐观更新✅ useOptimistic✅ 内置支持
请求去重
服务端状态分离
适用表单提交、简单异步复杂数据获取、缓存策略

结论:Actions 适合表单提交和简单异步操作。复杂的服务端状态管理仍推荐 TanStack Query。


三、use() Hook:统一资源消费

3.1 use() 的设计目标

React 19 之前,Context 和 Promise 的消费方式不一致:

  • Context:useContext(MyContext)
  • Promise:需要 use + Suspense 包装(实验性)

use() 统一了所有可挂起资源(Suspense-enabled resources)的消费。

3.2 use() 消费 Context

import { use } from 'react';
import { ThemeContext } from './ThemeContext';

function Button() {
  // 替代 useContext,可以在条件/循环中调用
  const theme = use(ThemeContext);
  return <button className={theme}>{/* ... */}</button>;
}

use() 与 useContext 的区别

  • use() 可以在 if/for 中调用(不像 hooks 规则严格)
  • use() 可以与 Suspense 配合挂起
  • useContext 仍可用,但 use() 是推荐的新 API

3.3 use() 消费 Promise

import { use, Suspense } from 'react';

function Comments({ commentsPromise }) {
  // use() 可以挂起组件,直到 Promise resolve
  const comments = use(commentsPromise);

  return (
    <ul>
      {comments.map((comment) => (
        <li key={comment.id}>{comment.text}</li>
      ))}
    </ul>
  );
}

function Post({ post }) {
  return (
    <div>
      <h1>{post.title}</h1>
      <Suspense fallback={<CommentsSkeleton />}>
        <Comments commentsPromise={fetchComments(post.id)} />
      </Suspense>
    </div>
  );
}

关键行为

  • use(promise) 首次调用时挂起组件
  • Promise resolve 后,React 重新渲染组件并返回结果
  • Promise reject 时,由最近的 Error Boundary 捕获

3.4 与 React Router Loader 的配合

import { use, Suspense } from 'react';
import { Await, useLoaderData } from 'react-router-dom';

function Dashboard() {
  const { user, stats, notifications } = useLoaderData();

  return (
    <div>
      <h1>Welcome, {user.name}</h1>

      {/* 旧方式:Await */}
      <Suspense fallback={<StatsSkeleton />}>
        <Await resolve={stats}>
          {(data) => <Stats data={data} />}
        </Await>
      </Suspense>

      {/* React 19 新方式:use() — 更简洁 */}
      <Suspense fallback={<NotificationsSkeleton />}>
        <Notifications promise={notifications} />
      </Suspense>
    </div>
  );
}

function Notifications({ promise }) {
  const notifications = use(promise);  // 直接解包 Promise
  return <NotificationList data={notifications} />;
}

四、React Compiler(React Forget):自动记忆化

4.1 手动优化的痛苦

React 18 之前的性能优化需要大量手动工作:

// ❌  React 18 的手动优化样板
function ExpensiveComponent({ data, onUpdate }) {
  const processed = useMemo(() =>
    data.map(item => heavyComputation(item)),
    [data]
  );

  const handleClick = useCallback((id) => {
    onUpdate(id);
  }, [onUpdate]);

  return <List items={processed} onClick={handleClick} />;
}

4.2 React Compiler 的原理

React Compiler(以前叫 React Forget)是一个 Babel 编译器插件,自动分析组件依赖并在编译时注入 memoization

// ✅ React 19 + Compiler:开发者只写业务逻辑
function ExpensiveComponent({ data, onUpdate }) {
  const processed = data.map(item => heavyComputation(item));
  // Compiler 自动包裹 useMemo

  const handleClick = (id) => {
    onUpdate(id);
  };
  // Compiler 自动包裹 useCallback

  return <List items={processed} onClick={handleClick} />;
}

编译后(伪代码)

function ExpensiveComponent({ data, onUpdate }) {
  const processed = useMemo(
    () => data.map(item => heavyComputation(item)),
    [data]  // Compiler 自动推导依赖
  );

  const handleClick = useCallback(
    (id) => { onUpdate(id); },
    [onUpdate]
  );

  return <List items={processed} onClick={handleClick} />;
}

4.3 配置 React Compiler

npm install -D babel-plugin-react-compiler
// babel.config.js
module.exports = {
  plugins: [
    ['babel-plugin-react-compiler', {
      runtimeModule: 'react/compiler-runtime',
    }],
  ],
};

目前状态:React Compiler 在 Meta 内部大规模使用,已通过 npm 发布但标注为 Beta。不建议在关键业务上完全依赖,但可以在新项目尝试。

4.4 Compiler 的规则与限制

Compiler 遵循闭包和不可变数据的假设:

  • 依赖必须是不可变的(推荐 Immer 或直接创建新对象)
  • 状态更新必须通过 React 官方 API(setStateuseReducer
  • ref.current 的修改不会被追踪(ref 是 escape hatch)
// ✅ Compiler 可以安全优化
const [count, setCount] = useState(0);
const doubled = count * 2;  // 自动 memoized

// ❌ Compiler 无法追踪(会跳过优化警告)
const ref = useRef({ count: 0 });
ref.current.count += 1;  // 直接修改 ref

五、Server Components 稳定版

5.1 Server Components 是什么(再确认)

Server Components(RSC)在 React 19 中脱离实验标记,成为推荐架构的一部分

  • 服务端组件在服务端执行,不进入客户端 bundle
  • 可以直接访问服务端资源(数据库、文件系统)
  • 通过流式 RSC Payload 发送到客户端

5.2 “use server” 指令

// actions.ts —— Server Actions
'use server';

export async function updateName(name: string) {
  // 在服务端执行,无需 API 路由
  await db.user.update({
    where: { id: getCurrentUserId() },
    data: { name },
  });

  revalidatePath('/profile');  // 重新验证缓存
}

// 客户端组件直接调用
'use client';

function ProfileForm() {
  return (
    <form action={updateName}>
      <input name="name" />
      <button>Update</button>
    </form>
  );
}

5.3 Server/Client 组件边界

Server Component
  ├── Server Component(可以)
  ├── Client Component(通过 props 传递序列化数据)
  │     └── Server Component(❌ 不能嵌套在 Client 中)
  └── Server Action(通过 props 传递函数引用)

关键规则

  • Server Component 可以 import Client Component
  • Client Component 不能 import Server Component(但可以通过 children prop 接收)
  • Server Action 可以在 Client Component 中直接调用

六、Document Metadata 与 Asset Loading

6.1 原生 Metadata 支持

React 19 允许在组件树任何地方声明 <title><meta><link>,React 自动提升到 <head>

function BlogPost({ post }) {
  return (
    <>
      <title>{post.title}</title>
      <meta name="description" content={post.excerpt} />
      <meta property="og:title" content={post.title} />
      <link rel="canonical" href={`https://site.com/posts/${post.id}`} />

      <article>
        <h1>{post.title}</h1>
        <div>{post.content}</div>
      </article>
    </>
  );
}

优势

  • SEO 相关标签与组件业务逻辑在一起
  • 不再依赖 react-helmet 等第三方库
  • 服务端渲染时正确输出到 <head>

6.2 样式表加载 Prefetching

function Component() {
  return (
    <>
      <link rel="stylesheet" href="styles.css" precedence="default" />
      <div>Content</div>
    </>
  );
}

precedence 属性控制样式优先级,React 自动处理去重和排序。

6.3 异步脚本加载

function Analytics() {
  return (
    <script
      async
      src="https://analytics.example.com/script.js"
    />
  );
}

React 19 原生支持异步脚本的加载去重和排序。


七、其他 API 改进

7.1 ref 作为普通 Prop

// ✅ React 19:ref 可以直接传递,不再需要 forwardRef
function Input({ ref, ...props }: { ref: React.Ref<HTMLInputElement> }) {
  return <input ref={ref} {...props} />;
}

// 使用
function Parent() {
  const inputRef = useRef<HTMLInputElement>(null);
  return <Input ref={inputRef} placeholder="Enter text" />;
}

7.2 Context 直接作为 Provider

// ✅ React 19:Context 本身就是 Provider
function App() {
  return (
    <ThemeContext value="dark">
      <Page />
    </ThemeContext>
  );
}

// 替代旧写法
// <ThemeContext.Provider value="dark">

7.3 新的 ref 回调清理

// React 19 的 ref 回调可以返回清理函数
function Component() {
  return (
    <div
      ref={(element) => {
        if (element) {
          console.log('mounted', element);
          return () => console.log('unmounted');  // 清理函数
        }
      }}
    />
  );
}

八、升级路径与兼容性

8.1 React 18 → 19 升级检查清单

检查项说明操作
依赖兼容性第三方库是否支持 React 19检查 package.json / npm ls
TypeScript类型定义更新npm update @types/react
useId可能的变化测试 SSR hydration
Context.Provider仍可用,但推荐简写逐步替换为 <Context value={...}>
forwardRef仍可用新代码直接用 ref prop
StrictMode新增第二次 render 行为测试是否有副作用
Compiler可选启用建议在新项目尝试

8.2 渐进式迁移策略

# 1. 升级依赖
npm install react@latest react-dom@latest

# 2. 升级类型
npm install -D @types/react@latest @types/react-dom@latest

# 3. 运行测试套件,修复废弃警告
npm test

# 4. 逐步采用新特性
# - 新组件用 use() 替代 useContext
# - 新表单用 Actions
# - 实验项目启用 Compiler

九、React 19 vs 框架对比

特性React 19Vue 3.4Svelte 5Solid 1.0
自动记忆化✅ Compiler✅ 响应式追踪✅ 编译时优化✅ 细粒度
服务端组件✅ RSC + Actions⚠️ 实验性⚠️ 有限
编译器优化✅ React Compiler✅ 编译器✅ 编译器
性能模型VDOM + CompilerVDOM + 编译优化无 VDOM无 VDOM
学习曲线中等中等

常见问题(FAQ)

React 19 后还需要 useMemo/useCallback 吗?

  • 短期(Compiler 成熟前):仍需在性能关键路径手动使用
  • 长期(Compiler 普及后):绝大多数场景不需要,Compiler 会自动处理
  • 特殊情况:自定义比较函数、跨库边界仍需手动控制

Actions 会替代 Redux/Zustand 吗?

不会。Actions 是异步操作的原生抽象,不是状态管理方案。Zustand/Redux 管理的是客户端状态树,Actions 处理的是异步副作用。两者互补——用 Zustand 管理状态,用 Actions 提交表单/调用 API。

Server Components 只能在 Next.js 用吗?

不是。RSC 是 React 的核心特性,但需要一个支持 RSC 的运行时。目前 Next.js App Router 是最成熟的实现。其他框架(Remix、Gatsby、Astro)也在集成中。

React Compiler 会取代手动优化吗?

在绝大多数场景下会。Compiler 的自动 memoization 覆盖约 95% 的优化场景。但以下情况仍需手动干预:

  • ref.current 变异追踪不到
  • 非 React 的副作用(直接操作 DOM、第三方库集成)
  • 需要自定义比较逻辑的深度优化

相关阅读

下一篇 →

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「frontend」更多文章