号外,React 19 终于发布了
时隔2年,React 19 终于在 2024-12-05 发布了 稳定版Stable!
一、新特性
1. Actions: useTransition
2. New hook: useActionState
3. React DOM: <form> Actions
4. React DOM: New hook: useFormStatus
5. New hook: useOptimistic
6. New API: use
二、New React DOM Static APIs
三、新的服务器组件
1. Server Components
2. Server Actions
四、参考文献
- React 官方博客: react.dev/blog/2024/1…
- RSC: react.dev/reference/r…
- RSA: react.dev/reference/r…
五、正文开始
1. Actions: useTransition
React 应用的常见用例之一是执行数据突变,然后更新状态作为响应。
比如,用户提交表单改名时,你会发出请求,然后处理响应。过去,你需要手动处理挂起状态、错误、乐观更新和串行请求。
举个栗子,以前可以在 useState 中处理挂起和错误状态:
// 使用 useState
function UpdateName({}) {
const [name, setName] = useState("");
const [error, setError] = useState(null);
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 (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}
React 19 支持在转换中使用异步函数,自动处理挂起状态、错误、表单和乐观更新。
现在,可以使用 useTransition 来处理挂起状态:
function UpdateName({}) {
const [name, setName] = useState("");
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();
const handleSubmit = () => {
startTransition(async () => {
const error = await updateName(name);
if (error) {
setError(error);
return;
}
redirect("/path");
})
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}
按照惯例,使用异步转换的函数称为“Actions”(动作)。
Actions 会自动管理提交的数据:
- 挂起状态:Actions 提供挂起状态,该状态在请求开始时启动,并在提交最终状态更新时自动重置。
- 乐观更新:Actions 新增
useOptimistichook,你可以在提交请求时向用户显示即时反馈。 - 错误处理:Actions 提供错误处理,在请求失败时显示错误边界,并自动将乐观更新恢复为其原始值。
- 表单:
<form>元素现在支持将函数传递给action和formActionprops。将函数传递给actionprops 默认使用 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>
}
2. New hook: useActionState
useActionState 接受一个 Action 函数,并返回一个包装 Action 来调用。
当调用包装 Action 时,useActionState 会返回 Action 的结果作为 data,并将其挂起状态返回为 pending。
const [error, submitAction, isPending] = useActionState(
async (previousState, newName) => {
const error = await updateName(newName);
if (error) {
// You can return any result of the action.
// Here, we return only the error.
return error;
}
// handle success
return null;
},
null,
);
3. React DOM: <form> Actions
Actions are also integrated with React 19’s new <form> features for react-dom. We’ve added support for passing functions as the action and formAction props of <form>, <input>, and <button> elements to automatically submit forms with 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。
4. React DOM: New hook: useFormStatus
In design systems, it’s common to write design components that need access to information about the <form> they’re in, without drilling props down to the component. This can be done via Context, but to make the common case easier, we’ve added a new hook useFormStatus:
在设计系统中,通常会编写需要访问其所在 <form> 信息的设计组件,而无需将 props 逐层传递到组件。
这可以通过 Context 来完成,但我们新增了一个简化 hook useFormStatus:
import {useFormStatus} from 'react-dom';
function DesignButton() {
const {pending} = useFormStatus();
return <button type="submit" disabled={pending} />
}
useFormStatus reads the status of the parent <form> as if the form was a Context provider.
For more information, see the react-dom docs for useFormStatus.
5. New 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 的值。
6 新 API:use
In React 19 we’re introducing a new API to read resources in render: use.
React 19 新增 use API 来读取渲染中的资源。
For example, you can read a promise with use, and React will Suspend until the promise resolves:
举个栗子,你可以使用 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 会报错。
You can also read context with use, allowing you to read Context conditionally such as after early returns: 你还可以使用 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>
}
The use API can only be called in render, similar to hooks. Unlike hooks, use can be called conditionally. In the future we plan to support more ways to consume resources in render with use.
类似于 hooks,use API 只能在渲染中调用。与 hooks 不同的是,use 可以条件调用。
菲鸽结语
新的 API 很香,准备在项目中使用,尤其是 useTransition + useOptimistic + use。愉快地搬砖吧~~
忘了说,最好结合 react-router 一起使用,里面也有很多好用的 api.