
在使用表单时,useActionState钩子简化了从表单中捕获值并将其作为 FormData 传递到服务器操作的过程。
useActionState还能自动管理状态更新,它会使用服务器操作返回的值来更新状态变量。这对于渲染输入字段的验证错误特别有用,下面的示例展示了如何与 Zod 验证库配合使用。
组件示例 (form.tsx)
"use client";
import { useActionState } from "react";
import { signUp } from "../actions";
export default function SignUp() {
const [state, action, isPending] = useActionState(signUp, {});
return (
<form action={action}>
<div>
<label htmlFor="username">用户名:</label>
<input
type="text"
id="username"
name="username"
defaultValue={state.username}
required
/>
{state.errors?.username && (
<p className="text-sm text-red-500">{state.errors.username}</p>
)}
</div>
<div>
<label htmlFor="password">密码:</label>
<input
type="password"
id="password"
name="password"
defaultValue={state.password}
/>
{state.errors?.password && (
<p className="text-sm text-red-500">{state.errors.password}</p>
)}
</div>
<input
type="submit"
value="注册"
disabled={isPending}
/>
</form>
);
}服务器操作 (actions.ts)
"use server";
import { z } from "zod";
const SignUpSchema = z.object({
username: z.string().min(1, "用户名不能为空"),
password: z
.string()
.min(8, { message: "密码长度至少为8个字符" })
.regex(/[a-zA-Z]/, { message: "必须包含至少一个字母" })
.regex(/[0-9]/, { message: "必须包含至少一个数字" })
.regex(/[^a-zA-Z0-9]/, {
message: "必须包含至少一个特殊字符",
})
.trim(),
});
export type SignUpActionState = {
username?: string;
password?: string;
errors?: {
username?: string[];
password?: string[];
};
};
export async function signUp(
prevState: SignUpActionState,
formData: FormData
): Promise<SignUpActionState> {
const username = formData.get("username") as string;
const password = formData.get("password") as string;
const validatedFields = SignUpSchema.safeParse({
username,
password,
});
if (!validatedFields.success) {
return {
username,
password,
errors: validatedFields.error.flatten().fieldErrors,
};
}
// 此处处理已验证的表单数据
return { username, password };
}关键功能说明
useActionState返回一个包含 isPending属性的元组,该属性指示服务器操作的 Promise 是否仍在解析中。您可以通过引用 isPending来临时禁用表单元素(例如提交按钮),以防止用户在操作完成前多次点击。
与 useFormAction 和 useFormStatus 的关系
如果您熟悉 useFormAction和 useFormStatus,会发现 useActionState与它们非常相似。本质上,它结合了两个钩子的功能,并重新命名以反映服务器操作不仅适用于表单(您还可以将 useActionState与按钮和其他元素一起使用)。
需要注意的是,从 Next.js 15 开始,useFormStatus已被弃用,建议使用 useActionState作为替代方案。