react中hooks之 React 19 新 Hooks useActionState useFormStatus用法总结
React 19 新 Hooks 使用指南: useActionState & useFormStatus
目录
- useActionState
- useFormStatus
- 最佳实践
useActionState
概述
useActionState 是 React 19 引入的新 Hook,用于处理表单 action 的状态更新。它允许你基于表单 action 的结果来更新组件状态。
官网:
基本语法
const [state, formAction, isPending] = useActionState(fn, initialState, permalink?);
参数说明
-
fn: 表单提交时调用的函数
- 接收上一次状态作为第一个参数
- 接收表单数据作为后续参数
- 返回新的状态
-
initialState: 初始状态值
- 可以是任何可序列化的值
- 在 action 首次调用后会被忽略
-
permalink?: (可选) 唯一页面 URL
- 用于动态内容页面(如 feeds)
- 配合渐进式增强使用
- 在 JavaScript bundle 加载前提交表单时使用
返回值
type UseActionStateReturn<T> = [
T, // 当前状态
(formData: FormData) => void, // 表单 action
boolean // 是否处于 pending 状态
];
使用示例
async function increment(previousState: number, formData: FormData) {
return previousState + 1;
}
function Counter() {
const [count, formAction, isPending] = useActionState(increment, 0);
return (
<form>
<p>Count: {count}</p>
<button formAction={formAction} disabled={isPending}>
{isPending ? 'Incrementing...' : 'Increment'}
</button>
</form>
);
}
useFormStatus
概述
useFormStatus 是一个专门用于获取父级表单提交状态的 Hook。它提供了表单提交过程中的详细状态信息。
基本语法
const { pending, data, method, action } = useFormStatus();
使用限制
- 必须在
<form>
元素内部使用 - 必须是表单的子组件
- 不能在表单 action 处理函数内使用
使用示例
function SubmitButton() {
const { pending, data } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
);
}
function Form() {
async function formAction(formData: FormData) {
// 处理表单提交
}
return (
<form action={formAction}>
<input name="name" />
<SubmitButton />
</form>
);
}
最佳实践
- Server Actions 集成
// 服务端 action
async function updateUser(prevState: any, formData: FormData) {
'use server';
const name = formData.get('name');
await db.updateUser({ name });
return { message: 'Updated!' };
}
// 客户端组件
function UserForm() {
const [state, formAction] = useActionState(updateUser, null);
return (
<form action={formAction}>
<input name="name" />
<SubmitButton />
{state?.message && <p>{state.message}</p>}
</form>
);
}
- 错误处理
async function submitForm(prevState: any, formData: FormData) {
try {
const result = await submitData(formData);
return { data: result, error: null };
} catch (e) {
return { data: null, error: e.message };
}
}
- 渐进式增强
function CommentForm({ postId }) {
const [state, formAction] = useActionState(
submitComment,
null,
`/posts/${postId}#comments` // permalink for progressive enhancement
);
return (
<form action={formAction}>
{/* 表单内容 */}
</form>
);
}
注意事项
-
useActionState:
- 与框架的 Server Components 集成时支持服务端渲染
- 函数签名与直接使用表单 action 不同
- 支持渐进式增强
-
useFormStatus:
- 只能在表单子组件中使用
- 提供实时的表单状态
- 适合构建可复用的表单组件
-
性能考虑:
- 自动处理并发更新
- 支持 Suspense 集成
- 优化服务端状态同步
最佳实践补充
- 状态复用
// 创建可复用的表单状态 hook
function useFormWithStatus<T>(action: string) {
const formStatus = useFormStatus();
const [formState, setFormState] = useState<T | null>(null);
useEffect(() => {
if (!formStatus.pending && formStatus.data) {
setFormState(Object.fromEntries(formStatus.data.entries()) as T);
}
}, [formStatus.pending, formStatus.data]);
return {
...formStatus,
formState
};
}
- 类型安全处理
// 为 useActionState 添加类型
interface UserData {
id: string;
name: string;
email: string;
}
const [updateUser, { data }] = useActionState<UserData, Partial<UserData>>(
async (updates) => {
const response = await fetch('/api/user', {
method: 'PATCH',
body: JSON.stringify(updates)
});
return response.json();
}
);
// TypeScript 会正确推断 data 的类型为 UserData | null
总结
-
useActionState 优点:
- 简化异步状态管理
- 提供完整的状态信息
- 易于集成错误处理
- 支持类型安全
-
useFormStatus 优点:
- 专注于表单状态
- 提供详细的提交信息
- 易于实现加载指示器
- 支持复杂表单流程
-
使用建议:
- 合理区分两个 Hook 的使用场景
- 实现适当的加载状态展示
- 做好错误处理
- 考虑状态持久化需求