这篇文章主要介绍了 React 19 的新功能,包括 Actions 自动处理数据突变相关状态,新增的 useActionState、useFormStatus、useOptimistic 等 hook,新的 use API 读取渲染资源,新的 React DOM 静态 API 生成静态站点,以及 React 服务器组件中的 RSC 和 RSA 等内容。同时提到后续会分享针对旧版功能的优化和改良。

关联问题: React 19 性能如何 RSC 如何使用 Actions 适用场景

00. Hello World

大家好,我是大家的 林语冰

React 18 发布两年半后,React 团队终于官宣,React 19 稳定版正式升级。

01. React 19 新功能

1.1 Action

React 应用的常见用例之一是执行数据突变,然后更新状态作为响应。

比如,用户提交表单改名时,你会发出请求,然后处理响应。过去,你需要手动处理挂起状态、错误、乐观更新和串行请求。

举个栗子,以前可以在 useState 中处理挂起和错误状态:

 1function UpdateName({}) {
 2  
 3  const [isPendingsetIsPending= useState(false)
 4
 5  const handleSubmit = async () => {
 6    setIsPending(true)
 7    const error = await updateName(name)
 8    setIsPending(false)
 9    if (error) {
10      setError(error)
11      return
12    }
13    redirect('/path')
14  }
15  
16}

React 19 支持在转换中使用异步函数,自动处理挂起状态、错误、表单和乐观更新。

现在,可以使用 useTransition 来处理挂起状态:

 1function UpdateName({}) {
 2  
 3  const [isPending, startTransition] = useTransition()
 4
 5  const handleSubmit = () => {
 6    startTransition(async () => {
 7      const error = await updateName(name)
 8      if (error) {
 9        setError(error)
10        return
11      }
12      redirect('/path')
13    })
14  }
15  
16}

异步转换会立即将 isPending 的状态设置为 true,发出异步请求,并在任何转换后将 isPending 切换为 false

按照惯例,使用异步转换的函数称为“Actions”(动作)。

Actions 会自动管理提交的数据:

  • 挂起状态:Actions 提供挂起状态,该状态在请求开始时启动,并在提交最终状态更新时自动重置。
  • 乐观更新:Actions 新增 useOptimistic hook,你可以在提交请求时向用户显示即时反馈。
  • 错误处理:Actions 提供错误处理,在请求失败时显示错误边界,并自动将乐观更新恢复为其原始值。
  • 表单<form> 元素现在支持将函数传递给 actionformAction props。将函数传递给 action props 默认使用 Actions,并在提交后自动重置表单。

React 19 构建于 Actions 之上,引入 useOptimistic 管理乐观更新,并新增了 React.useActionState hook 来处理 Actions 的常见情况。

react-dom 添加了 <form> Actions 自动管理表单,以及 useFormStatus 来支持表单动作的常见情况。

React 19 可以简化上述例子:

 1function ChangeName({ name, setName }) {
 2  const [error, submitAction, isPending] = useActionState(
 3    async (previousState, formData) => {
 4      const error = await updateName(formData.get('name'))
 5      if (error) return error
 6      redirect('/path')
 7      return null
 8    },
 9    null
10  )
11
12  return <form action={submitAction}>// ...</form>
13}

1.2 新 hook:useActionState

我们新增了 useActionState hook:

 1const [error, submitAction, isPending] = useActionState(
 2  async (previousState, newName) => {
 3    const error = await updateName(newName)
 4    if (error) {
 5      
 6      
 7      return error
 8    }
 9    
10    return null
11  },
12  null
13)

useActionState 接受一个 Action 函数,并返回一个包装 Action 来调用。

当调用包装 Action 时,useActionState 会返回 Action 的结果作为 data,并将其挂起状态返回为 pending

1.3 <form> Actions

React 19 还把 Actions 与 react-dom<form> 新功能集成,支持传递函数作为 <form><input><button> 元素的 actionformAction props,自动提交带有 Actions 的表单:

 1<form action={actionFunction}>

<form> Action 成功时,React 会自动重置非受控组件的表单。如果需要手动重置 <form>,可以调用新的 React DOM requestFormReset API。

1.4 新 hook:useFormStatus

在设计系统中,通常会编写需要访问其所在 <form> 信息的设计组件,而无需将 props 逐层传递到组件。

这可以通过 Context 来完成,但我们新增了一个简化 hook useFormStatus

 1import { useFormStatus } from 'react-dom'
 2
 3function DesignButton() {
 4  const { pending } = useFormStatus()
 5  return <button type="submit" disabled={pending} />
 6}

useFormStatus 读取父级 <form> 的状态,就好像该表单是 Context provider 一样。

1.5 新 hook:useOptimistic

执行数据突变的另一种常见 UI 模式是在异步请求正在进行时乐观显示最终状态。

React 19 新增了一个 useOptimistic 简化 hook:

 1function ChangeName({ currentName, onUpdateName }) {
 2  const [optimisticName, setOptimisticName] = useOptimistic(currentName)
 3
 4  const submitAction = async (formData) => {
 5    const newName = formData.get('name')
 6    setOptimisticName(newName)
 7    const updatedName = await updateName(newName)
 8    onUpdateName(updatedName)
 9  }
10
11  return (
12    <form action={submitAction}>
13      <p>{optimisticName}</p>
14      <input
15        name="name"
16        disabled={currentName !== optimisticName}
17      />
18    </form>
19  )
20}

updateName 请求正在进行时,useOptimistic hook 会立即渲染 optimisticName。当更新完成或出错时,React 会自动切换回 currentName 的值。

1.6 新 API:use

React 19 新增 use API 来读取渲染中的资源。

举个栗子,你可以使用 use 读取 promise 对象,React 会挂起直到该 promise 解析:

 1import { use } from 'react'
 2
 3function Comments({ commentsPromise }) {
 4  
 5  const comments = use(commentsPromise)
 6  return comments.map((comment) => <p key={comment.id}>{comment}</p>)
 7}
 8
 9function Page({ commentsPromise }) {
10  
11  
12  return (
13    <Suspense fallback={<div>Loading...</div>}>
14      <Comments commentsPromise={commentsPromise} />
15    </Suspense>
16  )
17}

use 不支持渲染中创建的 promise 对象。如果你将渲染中创建的 promise 传递给 use,React 会报错。

你还可以使用 use 条件式读取 context,比如在提前 return 之后:

 1import { use } from 'react'
 2import ThemeContext from './ThemeContext'
 3
 4function Heading({ children }) {
 5  if (children == null) {
 6    return null
 7  }
 8  
 9  
10  const theme = use(ThemeContext)
11  return <h1 style={{ color: theme.color }}>{children}</h1>
12}

类似于 hooks,use API 只能在渲染中调用。与 hooks 不同的是,use 可以条件调用。

02. 新的 React DOM 静态 API

react-dom/static 新增了两个 API 来生成静态站点:

  • prerender
  • prerenderToNodeStream

这些新 API 通过等待数据加载以生成静态 HTML 来改进 renderToString,为了与 Node Streams 和 Web Streams 等流环境配合使用。

举个栗子,在 Web Stream 环境中,你可以使用 prerender 将 React 树预渲染为静态 HTML:

 1import { prerender } from 'react-dom/static'
 2
 3async function handler(request) {
 4  const { prelude } = await prerender(<App />, {
 5    bootstrapScripts: ['/main.js'],
 6  })
 7  return new Response(prelude, {
 8    headers: { 'content-type': 'text/html' },
 9  })
10}

prerender API 会等待所有数据加载,然后再返回静态 HTML 流。

流可以转换为字符串,或者与流响应一起发送。它们在加载时不支持流内容,而现有的 React DOM 服务端渲染 API 支持流内容。

03. React 服务器组件

3.1 RSC(服务器组件)

RSC[2] 是一个新选项,允许在打包之前在与客户端应用或 SSR 服务器分开的环境中提前渲染组件。这个单独的环境是 RSC 中的“服务器”。

RSC 可以在构建时在 CI 服务器上运行一次,也可以使用 Web 服务器针对每个请求运行。

3.2 RSA(服务器动作)

RSA[3] 允许客户端组件调用在服务器上执行的异步函数。

当使用 "use server" 指令定义 RSA 时,你的框架会自动创建对服务器函数的引用,并将该引用传递给客户端组件。当客户端调用该函数时,React 将向服务器发送请求来执行该函数,并返回结果。

一个常见的误解是使用 "use server" 来表示 RSC,但其实没有针对 RSC 的指令。"use server" 指令只适用于 RSA。

RSA 可以在 RSC 中创建,并作为 props 传递给客户端组件,也可以在客户端组件中导入和使用。

高潮总结

React 19 是 React 18 时隔两年后发布的主版本升级,涉及的新功能较多,RSC 等部分技术细节需要大家进一步阅读文档。

参考文献

[1] React 官方博客: react.dev/blog/2024/1…

[2] RSC: react.dev/reference/r…

[3] RSA: react.dev/reference/r…

个人笔记记录 2021 ~ 2025