作者:
React 团队
译者:
林语冰
资源:React 官方博客[1]
免责声明:活人翻译,略有删改,仅供粉丝参考!
00. Hello World
大家好,我是大家的 林语冰
。
React 18 发布两年半后,React 团队终于官宣,React 19 稳定版正式升级。
01. React 19 新功能
1.1 Action
React 应用的常见用例之一是执行数据突变,然后更新状态作为响应。
比如,用户提交表单改名时,你会发出请求,然后处理响应。过去,你需要手动处理挂起状态、错误、乐观更新和串行请求。
举个栗子,以前可以在 useState
中处理挂起和错误状态:
// 旧版的写法:
function UpdateName({}) {
// const ...
const [isPending, setIsPending] = useState(false)
const handleSubmit = async () => {
setIsPending(true)
const error = await updateName(name)
setIsPending(false)
if (error) {
setError(error)
return
}
redirect('/path')
}
// return (...)
}
React 19 支持在转换中使用异步函数,自动处理挂起状态、错误、表单和乐观更新。
现在,可以使用 useTransition
来处理挂起状态:
// 新版的写法:
// 使用 Action 的挂起状态
function UpdateName({}) {
// const ...
const [isPending, startTransition] = useTransition()
const handleSubmit = () => {
startTransition(async () => {
const error = await updateName(name)
if (error) {
setError(error)
return
}
redirect('/path')
})
}
// return (...)
}
异步转换会立即将 isPending
的状态设置为 true
,发出异步请求,并在任何转换后将 isPending
切换为 false
。
按照惯例,使用异步转换的函数称为“Actions”(动作)。
Actions 会自动管理提交的数据:
- 挂起状态:Actions 提供挂起状态,该状态在请求开始时启动,并在提交最终状态更新时自动重置。
- 乐观更新:Actions 新增
useOptimistic
hook,你可以在提交请求时向用户显示即时反馈。 - 错误处理:Actions 提供错误处理,在请求失败时显示错误边界,并自动将乐观更新恢复为其原始值。
- 表单:
<form>
元素现在支持将函数传递给action
和formAction
props。将函数传递给action
props 默认使用 Actions,并在提交后自动重置表单。
React 19 构建于 Actions 之上,引入 useOptimistic
管理乐观更新,并新增了 React.useActionState
hook 来处理 Actions 的常见情况。
react-dom
添加了 <form>
Actions 自动管理表单,以及 useFormStatus
来支持表单动作的常见情况。
React 19 可以简化上述例子:
// 使用 <form> Actions 和 useActionState
function ChangeName({ name, setName }) {
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const error = await updateName(formData.get('name'))
if (error) return error
redirect('/path')
return null
},
null
)
return <form action={submitAction}>// ...</form>
}
1.2 新 hook:useActionState
我们新增了 useActionState
hook:
const [error, submitAction, isPending] = useActionState(
async (previousState, newName) => {
const error = await updateName(newName)
if (error) {
// 你可以返回 action 的任意结果
// 这里我们只返回错误
return error
}
// 处理成功情况
return null
},
null
)
useActionState
接受一个 Action 函数,并返回一个包装 Action 来调用。
当调用包装 Action 时,useActionState
会返回 Action 的结果作为 data
,并将其挂起状态返回为 pending
。
1.3 <form>
Actions
React 19 还把 Actions 与 react-dom
的 <form>
新功能集成,支持传递函数作为 <form>
、<input>
和 <button>
元素的 action
和 formAction
props,自动提交带有 Actions 的表单:
<form action={actionFunction}>
当 <form>
Action 成功时,React 会自动重置非受控组件的表单。如果需要手动重置 <form>
,可以调用新的 React DOM requestFormReset
API。
1.4 新 hook:useFormStatus
在设计系统中,通常会编写需要访问其所在 <form>
信息的设计组件,而无需将 props
逐层传递到组件。
这可以通过 Context 来完成,但我们新增了一个简化 hook useFormStatus
:
import { useFormStatus } from 'react-dom'
function DesignButton() {
const { pending } = useFormStatus()
return <button type="submit" disabled={pending} />
}
useFormStatus
读取父级 <form>
的状态,就好像该表单是 Context provider 一样。
1.5 新 hook:useOptimistic
执行数据突变的另一种常见 UI 模式是在异步请求正在进行时乐观显示最终状态。
React 19 新增了一个 useOptimistic
简化 hook:
function ChangeName({ currentName, onUpdateName }) {
const [optimisticName, setOptimisticName] = useOptimistic(currentName)
const submitAction = async (formData) => {
const newName = formData.get('name')
setOptimisticName(newName)
const updatedName = await updateName(newName)
onUpdateName(updatedName)
}
return (
<form action={submitAction}>
<p>{optimisticName}</p>
<input
name="name"
disabled={currentName !== optimisticName}
/>
</form>
)
}
当 updateName
请求正在进行时,useOptimistic
hook 会立即渲染 optimisticName
。当更新完成或出错时,React 会自动切换回 currentName
的值。
1.6 新 API:use
React 19 新增 use
API 来读取渲染中的资源。
举个栗子,你可以使用 use
读取 promise 对象,React 会挂起直到该 promise 解析:
import { use } from 'react'
function Comments({ commentsPromise }) {
// use 会挂起直到 promise 解析
const comments = use(commentsPromise)
return comments.map((comment) => <p key={comment.id}>{comment}</p>)
}
function Page({ commentsPromise }) {
// 当 use 在 Comments 中挂起时,
// 这个 Suspense 边界会被显示。
return (
<Suspense fallback={<div>Loading...</div>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
)
}
use
不支持渲染中创建的 promise 对象。如果你将渲染中创建的 promise 传递给 use
,React 会报错。
你还可以使用 use
条件式读取 context,比如在提前 return 之后:
import { use } from 'react'
import ThemeContext from './ThemeContext'
function Heading({ children }) {
if (children == null) {
return null
}
// 由于提前 return,
// 以前使用 useContext 这行不通。
const theme = use(ThemeContext)
return <h1 style={{ color: theme.color }}>{children}</h1>
}
类似于 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:
import { prerender } from 'react-dom/static'
async function handler(request) {
const { prelude } = await prerender(<App />, {
bootstrapScripts: ['/main.js'],
})
return new Response(prelude, {
headers: { 'content-type': 'text/html' },
})
}
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 等部分技术细节需要大家进一步阅读文档。
这篇官方博客主要分享了 React 19 的新功能,另外还有针对旧版功能的优化和改良,将在下一篇继续分享。
我是大家的 林语冰
👨💻,欢迎持续 关注,随时了解海内外前端开发的最新情报。
谢谢的大家点赞、留言和友情转发,我们下期再见~👍
参考文献
[1] React 官方博客: react.dev/blog/2024/1…
[2] RSC: react.dev/reference/r…
[3] RSA: react.dev/reference/r…