本公众号会持续更新技术干货,感谢大家支持,欢迎点赞、评论、留言。
之前介绍了React19的useOptimistic的用法和具体业务场景的实战,今天讲useActionState,useFormStatus。
我会整理出一些react19新的特性、新的api给大家详细分析以及其实战用法,不足之处还望各位大佬指出,一起探讨进步,感谢大家的支持~
1 为什么需要这两个Hook
在React 18及之前版本,管理表单需手动处理:
-
状态变量爆炸:需定义isSubmitting、error、data等多个useState
-
逻辑分散:提交逻辑、状态更新、错误处理分散在useEffect和事件回调中
-
组件耦合:子组件需通过props传递状态(如禁用按钮需父组件传isSubmitting)
React 19的解决方案:
-
useActionState:统一管理表单提交状态(包括pending状态、返回值、错误信息)
-
useFormStatus:在表单子组件内直接访问提交状态(无需props透传)
2 核心概念与API解析
先来看这两个hooks的用法:
2.1 useActionState
arduino
const [state, formAction, isPending] = useActionState(
actionFn, // 表单提交函数
initialState // 初始状态
);
- 参数:
- ctionFn(prevState, formData):接收上一次状态和表单数据,返回新状态
- initialState:状态初始值(可序列化数据)
- 返回值:
- state:当前状态(初始为initialState,提交后更新为actionFn返回值)
- formAction:绑定到 <form action/>或<button formAction/>的函数
- isPending:提交状态(true表示进行中)
2.2 useFormStatus
kotlin
const { pending, data } = useFormStatus();
- 返回值:
- pending:父级表单是否提交中(等价于useActionState的isPending)
- data:当前提交的表单数据(FormData类型)
- 关键限制:
- 必须作为<form>的直接子组件使用,否则无法获取状态
3 实战案例:用户评论表单
业务场景:
- 提交时显示Loading状态,禁用提交按钮
- 提交后显示成功/错误提示
- 在输入框组件中实时显示正在提交的内容
先来看react19(以下用nextJs为例)中的具体实现(使用新API):
先定义一个server-action.js文件,里面定义一个后台API的请求,这里用saveToDatabase(comment) mock了数据库操作,当然也可以是一个正常的后台API。
javascript
// 服务端Action (server-action.js)
'use server';
export async function submitComment(prevState, formData) {
const comment = formData.get('comment');
if (!comment.trim()) {
return { error: '评论内容不能为空!' };
}
try {
await saveToDatabase(comment); // 模拟数据库保存
return { message: '评论发布成功!' };
} catch (err) {
return { error: '提交失败,请重试' };
}
}
紧接着我们定义两个子组件,SubmitButton.jsx和CommentInput.jsx
javascript
import { useActionState } from 'react';
import { useFormStatus } from 'react-dom';
import { submitComment } from './server-action';
// 子组件:输入框(使用useFormStatus)
function CommentInput() {
const { pending, data } = useFormStatus();
return (
<div>
<label>您的评论:</label>
<input
name="comment"
disabled={pending}
placeholder={pending ? `正在提交: ${data?.get('comment')}` : ''}
/>
</div>
);
}
// 子组件:提交按钮(使用useFormStatus)
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? '提交中...' : '发表评论'}
</button>
);
}
然后是父组件CommentForm,要记住!useActionState这个方法是只能在client端使用的,所以在nextJs中记得加上'use client'在头部
javascript
'use client'
// 父组件:表单(使用useActionState)
export default function CommentForm() {
const [state, formAction, isPending] = useActionState(
submitComment,
{ message: null, error: null }
);
return (
<form action={formAction}>
<CommentInput /> {/* 子组件1:输入框 */}
<SubmitButton /> {/* 子组件2:按钮 */}
{/* 显示结果反馈 */}
{state.error && <p className="error">{state.error}</p>}
{state.message && <p className="success">{state.message}</p>}
</form>
);
}
代码解析:
- 状态自动传递
- useActionState管理全局状态(state、isPending)
- useFormStatus让深层子组件直接读取状态(无需props透传,直接👏🏻)
- 精准UI反馈
- 输入框组件CommentInput独立显示当前提交内容(通过data.get('comment'))
- 按钮自动禁用且文本变化(通过pending状态)
- 错误处理内聚
- 所有错误逻辑封装在submitComment中,返回结构化的state
4 与React 18实现的对比
以下为实现相同功能的React 18代码:
javascript
// React 18实现(手动状态管理)
function CommentForm() {
const [comment, setComment] = useState('');
const [isPending, setIsPending] = useState(false);
const [error, setError] = useState(null);
const [message, setMessage] = useState(null);
const handleSubmit = async (e) => {
e.preventDefault();
setIsPending(true);
setError(null);
try {
// 手动构造FormData
const formData = new FormData();
formData.append('comment', comment);
const result = await submitComment(formData);
setMessage(result.message);
} catch (err) {
setError(err.message);
} finally {
setIsPending(false);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>您的评论:</label>
<input
value={comment}
onChange={(e) => setComment(e.target.value)}
disabled={isPending}
/>
</div>
<button type="submit" disabled={isPending}>
{isPending ? '提交中...' : '发表评论'}
</button>
{error && <p className="error">{error}</p>}
{message && <p className="success">{message}</p>}
</form>
);
}
其实看到这里一眼就看出来优劣势了,react18中定义的变量实在太多了!
能力 | React18方案 | React19方案 | 优势 |
---|---|---|---|
状态变量数量 | 需4个useState | 1个useActionState统一管理 | 减少75%状态代码 |
子组件访问状态 | 需通过props传递isPending | 子组件直接使用useFormStatus获取 | 解除父子组件耦合 |
表单数据收集 | 需onChange+useState | 自动通过FormData获取 | 省去手动绑定 |
错误处理逻辑 | 分散在try/catch中 | 集中在Server Action返回 | 更易维护 |
代码量 | ~35行 | ~25行 | 减少30%+ |
5 最佳实践与升级建议
- 何时使用
- 简单表单:直接使用useActionState + 原生form表单
- 复杂UI:拆分组件 + useFormStatus获取局部状态
- 迁移(注意之前的useFormState被useActionState取代)
javascript
// 从React 18升级到19:
- import { useFormState } from 'react-dom'; // 废弃
+ import { useActionState } from 'react'; // 新API
- const [state, formAction] = useFormState(action, initialState);
+ const [state, formAction, isPending] = useActionState(action, initialState);
- 注意事项 * useFormStatus 必须 是<form>的直接子组件 * 避免在onSubmit中调用e.preventDefault()(会阻止Action执行)
通过上述模式,React 19将表单开发从手动管理推向声明式协作,覆盖了90%的日常表单场景,大幅提升开发效率与应用性能。
- React官方 useActionState
- React官方 useFormStatus