React Hook Form 与 Formik 深度对比:如何为 React 表单管理选择最佳方案
2026/9/11 7:42:51 网站建设 项目流程

React Hook Form 与 Formik 深度对比:如何为 React 表单管理选择最佳方案

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

表单是 Web 应用收集用户数据的核心手段,但在 React 这类前端框架中,表单的创建、样式与校验往往并不轻松。幸运的是,React Hook Form 与 Formik 两大开源库显著简化了这一过程。本文以二者为对比主体,先分别介绍它们的基本用法、优点与短板,再通过对照表、自定义校验、无障碍(Accessibility)实践三个维度展开深入对比,最后结合本仓库(refine)中 React Hook Form 适配器的真实源码与示例,说明当你在 CRUD 业务应用中需要“表单 + 数据交互”一体化能力时,应如何做出选择并落地实践。

阅读完本文,你将掌握:React Hook Form 与 Formik 的核心 API 与校验模型差异、二者在体积与维护状态上的客观差异,以及如何编写自定义校验和无障碍表单,并了解 Refine 如何通过@refinedev/react-hook-form适配器将 React Hook Form 无缝嵌入数据管理应用。

最流行的两个 React 表单库

React 生态中存在多个可用于表单管理的包,其中 React Hook Form 与 Formik 是最为流行的两个。本节将分别介绍它们的基本用法、优点与缺点。

React Hook Form

React Hook Form 是一个用于在 React 及 Next、Gatsby 等 React 框架中管理表单的库。它是一个免费、开源的库,采用 MIT 许可证,因此几乎可以按任何方式使用。你可以使用它管理表单状态与字段校验,也可以与 Material UI 等流行 UI 库集成。正如其名,React Hook Form 基于 React Hooks 构建,因此不能直接在 Class 组件中使用。

React Hook Form 的基本用法

在已有的 React 或 React Native 项目中,通过以下任一命令安装:

# npm npm install react-hook-form # yarn yarn add react-hook-form

下面的代码展示了一个使用useFormHook 的最基础登录表单。useForm是 React Hook Form 简化表单管理的关键 Hook,几乎总是会用到;它接收一个可选对象作为参数,并返回多个方法。其中最重要的是register函数,用于注册输入元素并应用校验规则:第一个参数是唯一的输入名称字符串,第二个可选参数用于添加校验字段(如requiredminLength)。

import { useForm } from "react-hook-form"; export function Form() { const { register, handleSubmit, watch, formState } = useForm(); const submitHandler = (data) => console.log(data); return ( <form onSubmit={handleSubmit(submitHandler)}> <p> <label htmlFor="username">Username</label> <input type="text" id="username" {...register("username", { required: true, minLength: 4 })} /> </p> <p> <label htmlFor="Password">Password</label> <input type="password" id="password" {...register("password", { required: true, minLength: 10 })} required /> </p> <button> Submit </button> </form> ); }
React Hook Form 的优点
  • 免费且开源。
  • 无依赖、体积小,根据 bundlejs 的统计,gzip 后体积约为 12.12KB。
  • MIT 许可证。
  • 易于上手,文档质量高。
  • 性能优秀。
  • 同时支持 React 与 React Native。
  • 开箱即用地校验表单字段。
  • 处于活跃维护状态,社区活跃。
  • 可与 Material UI 等 UI 库集成,也可以与 Refine 集成:在 Refine 中可通过@refinedev/react-hook-form适配器,在 CRUD 应用中使用useFormHook 处理表单。
React Hook Form 的缺点
  • 基于 React Hooks,因此不能直接在 Class 组件中使用。

Formik

Formik 是一个免费、流行的开源包,用于在 React 与 React Native 中构建表单。它内置了表单校验、错误消息与表单提交等管理能力,API 表面较小,即使是对 Formik 完全陌生的初学者也不难上手。

Formik 的基本用法

根据你使用的包管理器,可通过以下任一命令在 React 或 React Native 项目中安装:

# npm npm install formik # yarn yarn add formik

如果未使用 webpack 等打包器,也可以通过 HTMLscript标签从 unpkg CDN 加载:

<script src="https://unpkg.com/formik/dist/formik.umd.production.min.js"></script>

下面的代码展示了 Formik 构建登录表单的最基础用法。表单包含文本与 email 输入字段,使用了内置的FormFormikField组件:

  • Formik组件内部使用 render props,可以向其传入 render prop 或用于包裹子组件;除 render prop 外,Formik组件还接受许多其他 props。
  • Form是包裹 HTML form 元素的内置组件,内部可以访问传给FormikonSubmitprop 及其他 props。
  • Field是向表单添加input元素的内置组件。
import { Form, Formik, Field } from "formik"; export function LoginForm() { return ( <> <h1>Login form</h1> <Formik initialValues={{ userName: "", password: "" }} onSubmit={(values) => console.log("values ", values)} > <Form> <p> <label htmlFor="username">Username</label> <Field id="username" required type="text" name="userName" /> </p> <p> <label htmlFor="password">Password</label> <Field id="password" required type="password" name="password" /> </p> <button type="submit">Login</button> </Form> </Formik> </> ); }

Formik 将表单校验交给开发者自己:可以自行编写校验逻辑,也可以使用 Yup 等第三方库。除基础用法外,Formik 还提供了多种用于解决实际问题的复杂特性,可查阅其官方文档。

Formik 的优点
  • 同时支持 React 与 React Native。
  • 免费、性能良好。
  • 许可证灵活,基于 Apache License 2.0 发布。
  • 轻量:根据 bundlejs.com 的统计,gzip 后体积约为 44.34KB。
  • 文档优秀,易于上手。
  • 为 Ant Design、Material UI、Semantic UI 等流行 UI 框架提供了绑定。
Formik 的缺点
  • 目前维护并不活跃:截至撰写本文时,其仓库最近一次 Git 提交已是一年之前,同样至少一年没有发布新版本。

Formik 与 React Hook Form 对比总览

项目FormikReact Hook Form
Gzip 后打包体积44.34KB12.12KB
依赖数量70
活跃维护
性能良好良好
文档良好良好
许可证Apache License 2.0MIT
定价免费免费
社区支持良好良好

说明:上表中的 GitHub Stars、NPM 周下载量、开放/关闭 Issue 数量等数据为原文档写作时的快照(例如 Formik 约 31.6k Stars、NPM 周下载 2.1M,React Hook Form 约 32.2k Stars、周下载 2.7M)。这些社区指标会随项目发展持续变化,决策时应以最新数据为准;而体积、依赖数与维护活跃度则是最稳定的参考维度。

自定义校验:两种实现路径

自定义校验是表单开发中的高频需求。两个库都提供了清晰的实现方式,但思路不同。

React Hook Form:通过 register 的 validate 规则

React Hook Form 的自定义校验非常直接——在输入字段上通过register函数直接配置校验规则:

import { useForm } from "react-hook-form"; function MyForm() { const { register, handleSubmit, formState: { errors }, } = useForm(); const onSubmit = (data) => { console.log(data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("username", { required: "Username is required", validate: (value) => value.length >= 4 || "Username must be at least 4 characters", })} /> {errors.username && <p>{errors.username.message}</p>} <input {...register("email", { required: "Email is required", pattern: { value: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/, message: "Invalid email address", }, })} /> {errors.email && <p>{errors.email.message}</p>} <button type="submit">Submit</button> </form> ); }

在上面的示例中,我们为 username 与 email 字段设置了自定义校验消息。required可以直接携带字符串消息;validate接收一个返回布尔值或字符串的函数(返回字符串即错误消息);正则校验则通过patternvaluemessage配置。

Formik:通过 validate prop 集中校验

Formik 同样支持自定义校验,通过其validateprop 实现:把校验逻辑写在一个函数中并传给Formik

import { Formik, Form, Field, ErrorMessage } from "formik"; function MyForm() { return ( <Formik initialValues={{ username: "", email: "" }} validate={(values) => { const errors = {}; if (!values.username) { errors.username = "Username is required"; } else if (values.username.length < 4) { errors.username = "Username must be at least 4 characters"; } if (!values.email) { errors.email = "Email is required"; } else if ( !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email) ) { errors.email = "Invalid email address"; } return errors; }} onSubmit={(values) => { console.log(values); }} > {({ handleSubmit }) => ( <Form onSubmit={handleSubmit}> <Field name="username" /> <ErrorMessage name="username" component="div" /> <Field name="email" /> <ErrorMessage name="email" component="div" /> <button type="submit">Submit</button> </Form> )} </Formik> ); }

在这个例子中,我们通过validate函数实现了 username 与 email 字段的自定义校验,并使用内置的ErrorMessage组件渲染错误消息。两种方式各具特点:React Hook Form 把校验规则内联在字段上,粒度更细;Formik 集中管理校验逻辑,适合与 Yup 等 schema 校验库配合。

无障碍(Accessibility)实践

无障碍非常重要,它确保我们的应用可以被包括残障人士在内的所有人使用。以下是借助 React Hook Form 与 Formik 提升表单无障碍性的指南与示例。

React Hook Form 中的无障碍

使用 React Hook Form 时,应尽量使用语义化 HTML 元素并在必要时添加 ARIA 属性:

import { useForm } from "react-hook-form"; function AccessibleForm() { const { register, handleSubmit, formState: { errors }, } = useForm(); const onSubmit = (data) => { console.log(data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <div> <label htmlFor="username">Username</label> <input id="username" aria-invalid={errors.username ? "true" : "false"} {...register("username", { required: "Username is required" })} /> {errors.username && <span role="alert">{errors.username.message}</span>} </div> <div> <label htmlFor="email">Email</label> <input id="email" type="email" aria-invalid={errors.email ? "true" : "false"} {...register("email", { required: "Email is required", pattern: { value: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/, message: "Invalid email address", }, })} /> {errors.email && <span role="alert">{errors.email.message}</span>} </div> <button type="submit">Submit</button> </form> ); }

在这个示例中,我们使用aria-invalid标记无效字段,并使用role="alert"让屏幕阅读器播报错误消息。

Formik 中的无障碍

Formik 同样支持无障碍增强,通过正确使用语义化 HTML 元素与 ARIA 属性即可实现:

import { Formik, Form, Field, ErrorMessage } from "formik"; function AccessibleForm() { return ( <Formik initialValues={{ username: "", email: "" }} validate={(values) => { const errors = {}; if (!values.username) { errors.username = "Username is required"; } if (!values.email) { errors.email = "Email is required"; } else if ( !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email) ) { errors.email = "Invalid email address"; } return errors; }} onSubmit={(values) => { console.log(values); }} > {({ handleSubmit, errors, touched }) => ( <Form onSubmit={handleSubmit}> <div> <label htmlFor="username">Username</label> <Field id="username" name="username" aria-invalid={ errors.username && touched.username ? "true" : "false" } /> <ErrorMessage name="username" component="span" role="alert" /> </div> <div> <label htmlFor="email">Email</label> <Field id="email" name="email" type="email" aria-invalid={errors.email && touched.email ? "true" : "false"} /> <ErrorMessage name="email" component="span" role="alert" /> </div> <button type="submit">Submit</button> </Form> )} </Formik> ); }

在这个示例中,我们确保所有表单字段都有正确的 label 与 ARIA 属性,从而更易访问。注意 Formik 示例结合了touched状态来决定aria-invalid,避免字段尚未被用户触碰时就过早标记为无效。实现这些无障碍特性会让我们的表单对用户更友好。

在 Refine 中实战:@refinedev/react-hook-form 适配器

如果你的表单需求不仅限于“收集与校验”,还涉及数据资源的 CRUD 交互,那么 Refine 官方提供的 React Hook Form 适配器会把这一过程变得非常顺滑。安装方式:

npm install @refinedev/react-hook-form react-hook-form

其最小用法如下(package README):

import { useForm } from "@refinedev/react-hook-form"; const EditPost = () => { const { register, handleSubmit, formState, refineCore } = useForm({ refineCoreProps: { resource: "posts", id: "1", }, }); return; /* ... */ };

适配器如何融合两套 useForm

从源码看,适配器的核心实现在 packages/react-hook-form/src/useForm/index.ts:它同时调用 React Hook Form 自身的useHookForm与 Refine Core 的useFormCore,将二者合而为一。返回类型UseFormReturnType是 React Hook Form 的UseFormReturn与 Refine Core 表单能力的交叉类型,并额外提供:

  • refineCore:透出 Refine Core 的表单能力(onFinishformLoadingqueryonFinishAutoSave等),负责数据加载与提交;
  • saveButtonProps:包含disabled(提交期间禁用)与onClick的按钮属性,内部会调用handleSubmit并触发onFinish
  • handleSubmit:在 React Hook Form 原生handleSubmit之上包裹了一层,先关闭“未保存更改”警告再提交。

因此你既能享受 React Hook Form 的register/formState校验体验,又能直接获得 Refine 的onFinish数据提交能力。查看 examples/form-react-hook-form-use-form/src/pages/posts/edit.tsx 中的编辑页示例,可以看到典型的结合方式:refineCore.onFinish直接作为handleSubmit的回调、saveButtonProps绑定到提交按钮,useSelect加载关联分类数据并通过setValue回填category.id

配置项与服务器端校验

UseFormProps在 React Hook Form 原生 props 基础上扩展了三个配置项(见 packages/react-hook-form/src/useForm/index.ts):

  • refineCoreProps:传递给 Refine CoreuseForm的配置对象;
  • warnWhenUnsavedChanges:存在未保存更改并试图离开页面时,Refine 会弹出确认框,默认关闭;
  • disableServerSideValidation:是否禁用服务器端校验,默认false

服务器端校验是适配器的一个亮点:当onMutationError触发时(见 packages/react-hook-form/src/useForm/index.ts),适配器会遍历错误对象中的errors,针对已注册到表单的字段调用setError写入错误消息;错误值支持数组(拼接为字符串)、字符串、布尔值与带key/message的对象(后者会通过 Refine 的translate进行 i18n 翻译)。该行为在 useForm/index.spec.tsx 中有对应的测试用例,模拟了title: ["Title is required"]slug: truecontent: { key: "form.error.content", message: "Content is required" }等多种错误形态。

更多场景:Modal 表单与分步表单

适配器还提供两个扩展 Hook,均在useForm之上叠加能力:

  • useModalForm:为表单附加modal对象(submitcloseshow(id?)visibletitle),并支持modalProps配置defaultVisibleautoSubmitCloseautoResetFormautoResetFormWhenClose(默认分别为falsetruetruetrue),还支持与 URL 同步的表单状态;
  • useStepsForm:为表单附加steps对象(currentStepgotoStep),支持stepsProps配置defaultStep(默认 0)与isBackValidate(默认 false,即返回上一步时是否校验当前步骤)。

仓库中的完整示例可参考 examples/form-react-hook-form-use-modal-form 与 examples/form-react-hook-form-use-steps-form。

结论

表单管理是 Web 开发中一个容易出错的领域,尤其是在使用 React 这类前端框架时。HTML5 内置的表单功能在字段校验、错误管理与表单提交方面很有帮助;而 Formik 与 React Hook Form 这类解决方案进一步简化了 React 及其框架中的表单管理。两者都是流行、免费、开源、成熟且经过大量实战检验的优秀包,完全可以与原生 HTML5 表单功能配合使用。

另一方面,从 GitHub 上的提交与发布历史来看,Formik 目前维护并不活跃——撰写本文时其仓库已有一年没有新的 Git 提交或版本发布。此外,Formik 的打包体积相对更大(gzip 后约 44.34KB),而 React Hook Form 约为 12.12KB;Formik 内部依赖 7 个包,React Hook Form 则零依赖。

综合双方优缺点,如果你正准备在新项目中选择二者之一,React Hook Form 是更稳妥的选择。而如果你的应用是基于 Refine 构建的 CRUD 系统,@refinedev/react-hook-form适配器还能把表单校验与数据交互无缝打通,让 React Hook Form 的价值在真实业务场景中进一步放大。希望本文能帮助你为下一个项目做出合适的表单库决策。

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询