Next.js 表单验证与 useActionState
在 Next.js Server Action 表单中,用 FormData 接收不可信输入、运行时 Schema 验证,再用 useActionState 把 validation/result/pending 状态反馈给客户端 UI。
[!info] related notes
- 所属 MOC: nextjs-moc
- 前置概念: nextjs-server-actions、mutation-state-changing-operation
- 并列概念:
- 易混淆概念:
- 关系笔记:
Next.js 表单验证与 useActionState
一句话定义
Next.js 表单 Mutation 可以让 <form action={...}> 把 FormData 交给 Server Action;服务端先做运行时验证与授权,再把结构化结果返回给 useActionState,由客户端呈现 pending、字段错误和成功状态。
完整链路
<form>
→ FormData
→ Server Action
→ runtime validation
→ authentication / authorization
→ business rule
→ durable mutation
→ structured result
→ useActionState
→ error / pending / success UI
核心不是某个 Hook,而是让 不可信输入 → 可信业务输入 → 权威写入 → 可观察 UI 状态 形成一条完整链。
FormData 是输入载体,不是可信 DTO
浏览器提交的字段即使来自:
<input type="number" name="amount" />
服务端拿到的仍可能是字符串、缺失值或恶意构造值。
因此:
FormData
≠ trusted domain input
应该先转换并验证,例如使用 Zod:
const result = Schema.safeParse({
title: formData.get('title'),
})
if (!result.success) {
return {
ok: false,
fieldErrors: result.error.flatten().fieldErrors,
}
}
Validation、Authorization 与 Business Rule 是三层不同判断
Validation
→ 数据长得对不对?
Authentication / Authorization
→ 你是谁?你有资格操作这个资源吗?
Business Rule
→ 当前业务状态允不允许这么做?
例如合法 UUID 只证明格式正确,不能证明 Task 存在、属于当前用户或当前状态允许完成。
useActionState 管理什么
典型接口:
const [state, formAction, isPending] =
useActionState(action, initialState)
它把一次 Action 的结果和 pending 状态暴露给 UI。
配合后,Action 的典型签名会变成:
async function action(
previousState: State,
formData: FormData,
): Promise<State> {
// ...
}
previousState 是 React 管理的上一次 Action 结果,不是服务端数据库真值。
Expected Failure 与 Exception
表单校验失败、资源不存在、无权限、业务冲突通常属于可预期分支,更适合返回稳定的结构化 Result:
type Result =
| { ok: true; id: string }
| { ok: false; code: 'VALIDATION' | 'FORBIDDEN' | 'CONFLICT' }
数据库连接崩溃、程序 invariant 被破坏等不可预期问题,才更接近异常 / error boundary 的职责。
Pending 不是装饰动画
isPending 表示 Mutation 尚未得到确定结果,它影响:
- 是否禁用重复提交
- 是否显示提交中状态
- 是否允许某些交互继续
- 是否需要 optimistic state
因此 Pending 是 Mutation lifecycle 的正式状态,而不仅是 Spinner。
与 Optimistic UI 的关系
useActionState 与 optimistic state 解决的问题不同:
useActionState
→ 追踪 Action 的真实返回结果与 pending
optimistic UI
→ 在服务器确认前先显示预测状态
服务器失败时撤销预测叫 rollback;服务器成功但返回了真实 ID、时间戳等字段时,用权威结果替换临时对象属于 reconciliation,而不是 rollback。
最重要的边界
Server Action 在服务器运行
≠ 参数可信
Zod 验证通过
≠ Mutation 已被授权
UI 显示成功
≠ durable state 已经成功写入
服务端仍是最终业务状态 authority。
课程位置
对应 Full Stack Open Part 14 中的:
- Validations in blog creation
- Blog creation form on error
- Validations in user registration
- Returning validation errors with useActionState
并与 Next.js Server Actions、Mutation(状态变更操作) 共同构成表单写操作主线。