485 字
2 分钟
useFormStatus

它读的是父级表单的提交状态#

import { useFormStatus } from 'react-dom'
function Submit() {
const { pending } = useFormStatus()
return (
<button type="submit" disabled={pending}>
{pending ? '提交中……' : '提交'}
</button>
)
}
export default function Form() {
return (
<form action={submitForm}>
<input name="email" />
<Submit />
</form>
)
}

导入来源是 react-dom,不是 react,这一点和别的 Hook 不一样。它不接受参数。

为什么必须拆成子组件#

这是它最容易踩的坑:useFormStatus 只认父级 <form>,不认自己所在组件渲染出来的那个表单。

// 不行:pending 永远是 false
function Form() {
const { pending } = useFormStatus()
return (
<form action={submit}>
<button disabled={pending}>提交</button>
</form>
)
}

同一个组件里渲染的 <form> 它看不到,子组件里的 <form> 也不看。所以提交按钮必须抽成单独的组件,放进表单内部,像上面第一个例子那样。

四个返回值#

const { pending, data, method, action } = useFormStatus()

pending 是布尔值,表示父级表单是否正在提交。

data 是正在提交的 FormData,没有进行中的提交(或没有父级表单)时是 null。可以用它做「正在提交 xxx」这类提示。

method 是 'get' 或 'post'。

action 指向父级表单 action 上那个函数;如果写的是 URL,或者根本没写,这里是 null。

与 useActionState 的分工#

两者经常一起用,但它们站的位置不同。

useActionState 用在持有状态的那一层:拿动作的返回值、拿 isPending、决定显示什么错误。见 useActionState。

useFormStatus 用在表单内部的子组件:按钮、输入框这类只需要知道「现在能不能点」的地方。它省掉了把 isPending 一层层传下去的麻烦。

一条边界#

它读的是「最近一次表单提交」的状态,不是「某个动作」的状态。同一个表单里同时发起两件互不相关的异步操作时,别指望它能分清,那种情况要给每个动作自己带状态。

第 32 篇,共 39 篇本文属于系列

useFormStatus
https://www.choria.top/posts/hook-use-form-status/
作者
乔瑞雅·克林威尔
发布于
2026-05-02
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取

最后更新于 ,距今已过 190 天

部分内容可能已过时