首页 / 文章 / 使用 Zod 实现 React 19 的 Server Actions:减少 FormData 的样板代码

使用 Zod 实现 React 19 的 Server Actions:减少 FormData 的样板代码

虽然 React Hook Form 仍更胜一筹,但也可以将 React 19 的 Server Actions 与 Zod 结合使用,同时还有一个小型辅助工具可以避免重复解析 FormData。

1168 词

React 19、Server Actions以及useActionState彻底改变了表单的构建方式。对于许多界面,你可以继续使用原生HTML表单和服务器端处理逻辑,而无需为每种情况都引入庞大的客户端表单库。

在多次迁移到该技术栈后,几乎每个操作中都会出现同样的两行代码:

const data = Object.fromEntries(formData);
const result = schema.safeParse(data);if (!result.success) {
  return {
    errors: result.error.flatten().fieldErrors,
  };
}

登录页面、注册流程、联系表单以及个人资料编辑器都遵循相同的处理逻辑:将FormData转换为对象,使用Zod进行验证,处理字段错误,然后再继续后续操作。正是这种重复性工作成为了zod-form-action要解决的问题——它是一个小型辅助工具,用于将Zod与React 19 Server Actions结合使用。

React 19有哪些变化?

useActionState 能让提交状态更易于追踪,无需手动处理多个客户端状态信息。

不必为每个表单在浏览器中分别实现 onSubmit、加载标志和验证功能,表单可以直接发送到服务器端处理。

这种做法通常能带来以下优势:

  • 发送到客户端的 JavaScript 代码更少
  • 简单流程下的性能表现更好
  • 操作代码更简洁
  • 在客户端资源加载缓慢或缺失时仍能实现渐进式增强
  • UI 功能与服务器处理逻辑的划分更为清晰

对于许多应用而言,这种原生方案非常合适。

表单验证的问题

服务器端处理函数会接收到一个 FormData 实例。

在 Zod 进行验证之前,大多数处理程序会先将该实例转换为普通对象。

const data = Object.fromEntries(formData);
const result = schema.safeParse(data);

这种方法本身没有问题。

问题在于每个处理函数都需要执行相同的检查清单:

  • 将 FormData 转换为普通对象
  • 运行 Zod 模式验证
  • 格式化字段级的错误信息
  • 成功时返回类型化的值
  • 继续执行业务逻辑

当这种检查清单出现在数十个表单中时,繁琐的重复工作会挤占真正的业务逻辑空间。

介绍 zod-form-action

zod-form-action 的存在就是为了减少这些重复性工作。

处理函数无需在每个服务器操作中重新实现解析和验证功能,从而能够专注于业务逻辑的实现。

该包要求已使用以下技术栈:

  • React 19
  • useActionState
  • Next.js Server Actions(或其他服务器操作框架)
  • Zod
  • TypeScript

目标很明确:保持服务器端表单验证的一致性并便于重复使用。

可通过此 CodeSandbox 演示查看实时沙箱环境。

为何不直接使用 React Hook Form?

这个问题经常被提出,答案也并非全盘否定。

React Hook Form 依然是一个出色的库。

它在以下场景仍是绝佳选择:

  • 动态表单
  • 客户端验证
  • 复杂的交互流程
  • 字段数组
  • 多步骤向导

并非所有表单都需要如此复杂的功能。

当项目已采用 React 19 的原生表单工作流及 Server Actions 时,服务器端简单的 Zod 辅助工具可能比完整的客户端表单引擎更合适。

这两种工具解决的是不同的问题,而非直接竞争关系。

使用 zod-form-action 的优势

团队通常会采用它来:

  • 无需重复编写 FormData 转换代码
  • 确保各操作间的验证行为一致
  • 解析成功后可获得更清晰的 TypeScript 类型定义
  • 以统一格式报告字段错误
  • 能与 React 19 Server Actions 无缝集成
  • 让每个操作专注于对应领域的规则处理

实际应用场景

它可完美应用于日常界面:

  • 登录
  • 注册
  • 联系我们
  • 订阅新闻简报
  • 个人资料编辑
  • 账户设置
  • 控制面板输入框
  • 管理员 CRUD 表单

这类流程通常只需要简单的重复校验,而非高度动态的字段结构。

为何要提取该模式

几乎所有迁移后的代码库中都会出现几乎相同的验证模块。

永久复制这些代码存在脆弱性;将共享的步骤封装到可重用的包中,既能保持项目一致性,也能让其他人对该辅助工具进行改进。

发布该包可以吸引反馈、错误报告以及贡献,而不会将这种模式限制在某个私有仓库中。

类型安全很重要

Zod 的优势在于结合了运行时检查与完善的 TypeScript 类型定义。

无需始终将每个 FormData 值视为未知数据,该工具可以直接处理已经过验证并具有明确类型的数据。

这通常意味着:

  • 成功处理路径下的自动补全功能更完善
  • 运行时出现意外情况的情况更少
  • 处理逻辑更加简洁
  • 长期维护更为容易

性能

由于验证在服务器端执行,客户端代码包的体积可以保持更小。

对于已经采用 Server Actions 的应用而言,这符合 React 19 在有助于提升性能时将处理任务移至服务器的倾向。

下一步计划?

zod-form-action 的后续开发重点在于提升开发者体验。

正在探索的领域包括:

  • 更强大的 TypeScript 推断功能
  • 更多验证辅助工具
  • 更多示例代码
  • 更广泛的框架集成支持
  • 更清晰的文档说明

使用该包的用户反馈将指导后续版本的发布方向。

常见问题

我可以在 React 19 中使用 React Hook Form 吗?

可以。当表单需要强大的客户端交互功能时,React Hook Form 依然是一个可靠的选择。

React 19 会取代 React Hook Form 吗?

不是的。React 19只是新增了相关 API 和设计模式,并没有废弃现有的表单库。选择哪种工具应依据表单的复杂程度以及验证的执行位置来决定。

为何选择 Zod 进行验证?

Zod 能在运行时进行验证,并使 TypeScript 类型与验证结果保持一致,这样无效的数据会在早期就被拦截,而有效数据则仍能保持正确的类型。

zod-form-action 仅适用于 Next.js 吗?

并非如此。任何使用 Server Actions 和 Zod 的 React 19 应用,只要架构设计得当,都能从中受益。

总结

React 19 推动了团队采用更简单的表单构建方式。

通过结合原生 HTML 表单、useActionState、Server Actions 以及 Zod,无需依赖庞大的客户端表单工具包,就能实现强大且类型安全的验证功能。

当相同的 FormData 解析与验证逻辑出现在多个操作中时,将其提取为可重用的辅助函数能提升代码库的维护性。

zod-form-action 正是围绕这一目标开发的。

可从 npm 包页面进行安装。