React 19 核心新特性全解:Actions(useActionState / useOptimistic)、use() API 与 ref 架构简化是如何协同的?
React 19 围绕 异步数据突变(Actions)状态自动化、一等公民 Promise 读取原语 use() 以及 消除历史样板代码(如 forwardRef) 进行了全面升级。
一、 React 18 手写异步表单 vs React 19 Actions 原语对比
在 React 18 中处理一次表单提交,开发者必须手动维护 isPending、error、optimisticData 三个状态;React 19 将支持 async 函数的过渡更新统称为 Actions,并提供了三大配套 Hook:
| React 19 新 API | 核心职责与解决的痛点 |
|---|---|
useActionState(fn, initialState) |
自动接管 Action 的 pending 状态、上一次返回值 state 及包裹后的提交函数 formAction,可直接绑定到 <form action={formAction}> |
useFormStatus() |
在表单内部的深层子组件(如通用 <SubmitButton />)中直接读取外层 <form> 的 { pending, data },无需层层透传 loading Prop |
useOptimistic(state, updateFn) |
在异步 Action 进行期间立即渲染乐观状态;一旦异步 Action 结束或失败,自动无感回退或对齐真实状态 |
use(resource) |
可在组件渲染期间(甚至支持写在 if 条件分支和循环内部!)直接读取 Promise(配合 <Suspense>)或读取 Context |
ref 作为普通 Prop |
函数组件直接通过 function MyInput({ placeholder, ref }) 接收 ref,彻底淘汰 React.forwardRef 包装器 |
二、 React 19 完整代码实战:Actions + 乐观更新 + use()
import { useActionState, useOptimistic, use } from 'react'
import { useFormStatus } from 'react-dom'
// 1. 通用提交按钮:自动感知所属 <form> 的提交状态,无需父组件传 loading
function SubmitButton() {
const { pending } = useFormStatus()
return <button type="submit" disabled={pending}>{pending ? '发布中...' : '发表评论'}</button>
}
// 2. 使用 useActionState + useOptimistic 实现零样板代码的乐观评论列表
export function CommentSection({ commentsPromise }: { commentsPromise: Promise<string[]> }) {
// use() 直接解包父级传入的 Promise(并支持写在条件判断后!)
const initialComments = use(commentsPromise)
const [comments, formAction] = useActionState(
async (prevComments: string[], formData: FormData) => {
const text = String(formData.get('comment') || '').trim()
await saveCommentToServer(text) // 真实异步请求
return [...prevComments, text]
},
initialComments
)
const [optimisticComments, addOptimistic] = useOptimistic(
comments,
(state, newComment: string) => [...state, `${newComment} (发送中...)`]
)
return (
<div>
<ul>{optimisticComments.map((c, i) => <li key={i}>{c}</li>)}</ul>
<form
action={async (formData) => {
addOptimistic(String(formData.get('comment') || ''))
await formAction(formData)
}}
>
<input name="comment" required />
<SubmitButton />
</form>
</div>
)
}