
它管的是「动作的结果」
const [state, dispatchAction, isPending] = useActionState( reducerAction, initialState, permalink?)三个返回值分别是:当前状态、触发函数、进行中标记。
第一个参数 reducerAction(previousState, actionPayload) 与 useReducer 的 reducer 形状相同,但有一条关键差别:它可以异步,也可以有副作用。官方的说法是把 useActionState 当成「带副作用的 useReducer」。
它是 React 19 新增的(实验期叫 ReactDOM.useFormState,稳定时改名)。
一个例子
import { useActionState } from 'react'
async function updateName(prevState, formData) { const name = formData.get('name') if (!name) { return '名字不能为空' } await saveName(name) return null}
function NameForm() { const [error, dispatchAction, isPending] = useActionState(updateName, null)
return ( <form action={dispatchAction}> <input name="name" disabled={isPending} /> <button type="submit">保存</button> {error && <p>{error}</p>} </form> )}把 dispatchAction 直接交给 <form action>,React 会替你包一层过渡,成功后表单还会自动重置。
注意参数的错位:普通的表单 action 第一个参数是 FormData,用了 useActionState 之后它变成第二个,第一个固定是上一次的状态。
五条规则
dispatchAction 必须在过渡里调用。传给 <form action> 或组件的 action prop 时 React 自动处理;自己手动调,就要用 startTransition 包住,否则 isPending 不更新,控制台还会报「called outside of a transition」。
队列是串行的。每次调用都会等到上一次结束,并把上一次的返回值当作 previousState 传进来。官方说这是有意设计:不给上一个结果就没法算下一个。代价是连点四下就排四次队、依次执行。要在等待期间立即给反馈,配 useOptimistic。
已知错误用返回值,未知错误才抛。校验失败之类的可预期错误,return 成状态显示在界面上;真正异常(代码 bug)抛出去,React 会取消队列里剩下的动作并交给最近的错误边界。
它没有内置的重置函数。想清空,就在 reducerAction 里认一个约定的重置信号;或者给组件加 key 强制重挂载;用 <form action> 时提交成功本来就会重置。
不要在渲染期间调用 dispatchAction。那会造成渲染、派发、再渲染的循环,React 会直接报错拦住。
另外一条与 useReducer 相反的细节:reducerAction 在严格模式下不会被调用两次,因为它本来就允许有副作用。
与 useReducer 的分工
官方把界限划得很清楚:useReducer 管界面状态,reducer 必须是纯函数;useActionState 管动作的状态,reducer 允许异步与副作用。需要并行执行的动作,用 useState 加 useTransition 自己做,别硬塞进这个 Hook。
服务端渲染
配 Server Functions 时,初始状态需要可序列化(普通对象、数组、字符串、数字)。第三个参数 permalink 是给渐进增强用的:JavaScript 还没加载完就提交表单时,浏览器会导航到那个地址,而不是当前页面。这一层通常由框架处理,业务代码很少直接写。
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 190 天
部分内容可能已过时