React Hook Form 中文指南:高性能 React 表单状态管理与校验实战
【免费下载链接】react-hook-form📋 React Hooks for form state management and validation (Web + React Native)项目地址: https://gitcode.com/gh_mirrors/re/react-hook-form
导读
本文以 React Hook Form 项目的中文 README(docs/README.zh-CN.md)为骨架,系统讲解这款面向 React 生态(Web 与 React Native)的表单状态管理与校验库:从安装、useForm快速上手,到非受控架构、原生 HTML 校验、register校验规则、handleSubmit提交流程,再到 Yup/Joi/Superstruct 等 Schema 校验与 UI 库集成。结合本仓库源码(src/useForm.ts、src/logic/createFormControl.ts 等)与 examples/V7 真实示例,读者将掌握其 API 用法、底层运行原理与最佳实践。
一、React Hook Form 是什么
React Hook Form 是面向 React 的表单状态管理与校验库,用 React Hooks 驱动,适用于 Web 与 React Native。它的核心设计目标体现在中文文档的「特性」列表中:
- 使创建表单和集成更加便捷:Hooks 化 API 极大降低表单样板代码。
- 非受控表单校验:不依赖受控组件与逐字段
onChange重渲染,性能更好。 - 以性能和开发体验为基础构建:源码中
useForm通过useRef持有表单控制实例(见 src/useForm.ts),避免每次渲染重建。 - 迷你体积且零依赖:
package.json中无运行时依赖,配合 tree-shaking 体积极小。 - 遵循 HTML 标准校验:直接复用浏览器原生约束校验能力。
- 兼容 React Native:无 DOM 依赖,仓库中
src/index.react-server.ts等入口亦兼顾服务端渲染。 - 支持 Yup、Joi、Superstruct 或自定义 Schema 解析器(新版 README 亦提及 Zod、AJV)。
- 支持浏览器原生校验:可关闭,也默认开启并映射为表单错误。
- 提供 Form Builder 可视化构建表单(官方站点功能)。
注:本仓库当前版本为 v7 系列代码(
examples/V7、src/中的 v7 实现),README.zh-CN.md 的示例为早期 v6 写法;下文会同时给出两种写法对照,并说明其差异,帮助读者在实际项目中正确使用。
二、安装与版本说明
在任意 React 项目中安装:
$ npm install react-hook-formpnpm 或 yarn 用户可分别使用pnpm add react-hook-form、yarn add react-hook-form。本仓库的package.json中react-hook-form的运行时依赖列表为空,印证了“零依赖”特性。
安装后即可在组件中导入核心 Hook:
import { useForm } from 'react-hook-form';React Hook Form 同时提供useFormContext、useWatch、useFieldArray、Controller等 API,详见 src/index.ts 的导出。
三、快速开始:第一个表单
中文 README 给出了最简示例(v6 写法,通过ref={register}注册字段):
import React from 'react'; import { useForm } from 'react-hook-form'; function App() { const { register, handleSubmit, errors } = useForm(); // 初始化 hook const onSubmit = (data) => { console.log(data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="firstname" ref={register} /> {/* 注册一个输入框 */} <input name="lastname" ref={register({ required: true })} /> {errors.lastname && 'Last name is required.'} <input name="age" ref={register({ pattern: /\d+/ })} /> {errors.age && 'Please enter number for age.'} <input type="submit" /> </form> ); }v7 推荐写法(当前仓库主 README 与 examples/V7/basic.tsx 采用的写法):register返回展开属性,错误统一收敛到formState.errors:
import { useForm } from 'react-hook-form'; function App() { const { register, handleSubmit, formState: { errors }, } = useForm(); return ( <form onSubmit={handleSubmit((data) => console.log(data))}> <input {...register('firstName')} /> <input {...register('lastName', { required: true })} /> {errors.lastName && <p>Last name is required.</p>} <input {...register('age', { pattern: /\d+/ })} /> {errors.age && <p>Please enter a number for age.</p>} <input type="submit" /> </form> ); }核心 API 语义:
useForm():返回表单控制方法、状态与register。register(name, rules?):注册表单字段并挂载校验规则,v7 中通过展开运算符把name、onChange、onBlur、ref绑定到输入元素。handleSubmit(onValid, onInvalid?):校验通过时调用onValid(data),失败时调用onInvalid(errors)。errors(v6)/formState.errors(v7):字段错误对象,errors.lastname存在即表示该校验未通过。
从源码看useForm的初始化流程
src/useForm.ts 展示了核心实现:useForm内部用React.useRef缓存表单控制实例(首次创建后不再重建),并通过createFormControl(props)创建控制逻辑(src/logic/createFormControl.ts)。默认选项定义在createFormControl顶部:
const defaultOptions = { mode: VALIDATION_MODE.onSubmit, // 默认 onSubmit 模式 reValidateMode: VALIDATION_MODE.onChange, shouldFocusError: true, // 提交失败自动聚焦第一个错误字段 } as const;即:不传任何配置时,表单在onSubmit时校验,提交后再校验发生在onChange,且出错自动聚焦。
四、非受控架构与性能原理
React Hook Form 采用**非受控(uncontrolled)**设计:字段值由 DOM 自身持有,React Hook Form 只在校验/提交/watch时读取。这与受控组件(每个输入都绑定value+onChange+ 每次输入都触发重渲染)形成鲜明对比,避免了“每敲一个字符就重新渲染整个表单”的性能问题。
从源码结构看,createFormControl内部维护_fields(字段引用集合)、_formValues(表单值镜像)、_formState(表单状态)与基于createSubject(src/utils/createSubject.ts)的订阅发布系统;只有订阅了对应状态的组件才会在状态变化时重新渲染(shouldRenderFormState、shouldSubscribeByName等逻辑负责按需通知)。因此,非受控 + 按需订阅是它保持高性能的两大基石。
适用场景:
- 追求极致性能、表单字段多的大型页面;
- 需要最小化重渲染次数的场景;
- 与现有非受控 UI 或原生表单元素配合。
局限:如果业务强依赖受控(例如实时联动、需要强制刷新 UI 展示值),可使用Controller/useController包装受控组件(仓库 examples/V7/typescript/Control.tsx 展示了Control类型的传参方式)。
五、register 与校验规则详解
register是字段注册与规则挂载的入口。v7 中第二个参数为校验规则对象,结合 src/constants.ts 中定义的INPUT_VALIDATION_RULES,内置规则如下:
| 规则 | 说明 | 示例 |
|---|---|---|
required | 必填,值为true或错误消息 | { required: true }/{ required: '请输入' } |
min/max | 数值(或日期字符串)最小值/最大值 | { min: 10 }、{ max: 20 }、{ min: '2019-08-01' } |
minLength/maxLength | 字符串/数组最小/最大长度 | { minLength: 2, maxLength: 80 } |
pattern | 正则匹配 | { pattern: /\d+/ } |
validate | 自定义校验函数或函数对象 | { validate: (v) => v === 'test' } |
仓库演示应用 app/src/basic.tsx 几乎覆盖了全部规则,且展示了几类关键细节:
- 嵌套字段:
register('nestItem.nest1', ...),错误读取为errors.nestItem?.nest1; - 数组字段:
register('arrayItem.0.test1', ...),错误读取为errors.arrayItem?.[0]?.test1; - 日期边界:
<input type="date">配合min/max字符串比较; - 单选/多选:radio 组同名注册、checkbox 数组同名注册、
<select multiple>注册; - 自定义校验:
validate: (value) => value === 'test'。
浏览器原生校验的桥接
React Hook Form 遵循 HTML 标准校验:规则会被映射到 DOM 的required、min、max、pattern等属性上,由浏览器先行校验;同时库自身也会在validateField(src/logic/validateField.ts)中统一执行规则判定并生成FieldError。这种双保险让错误信息、焦点管理完全由 React Hook Form 接管,展示一致的 UI。
完整校验示例(v7 写法)
完整示例可参考 examples/V7/basicValidation.tsx,其中包含文本框、数字、select、radio 与pattern组合的完整表单。核心片段:
<input {...register('email', { required: true, pattern: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, })} /> {errors.email && 'Email is invalid'}六、handleSubmit:提交流程与错误处理
handleSubmit接收两个回调:校验成功回调onValid(data, event)与失败回调onInvalid(errors, event)(可选)。v7 中失败回调从useForm()参数迁移到了handleSubmit第二参数(对比 docs/README.V6.md 与 v7 写法)。app/src/basic.tsx 演示了onInvalid的计数用法。
源码级提交流程
src/logic/createFormControl.ts 中handleSubmit的执行链路:
- 若有事件对象,调用
e.preventDefault()阻止默认提交; - 通知订阅者
isSubmitting: true; - 若配置了
resolver,调用_runSchema()执行 Schema 校验(_resetCallId防竞态);否则执行内置校验executeBuiltInValidation; - 从结果中剔除
disabled字段的值(_names.disabled); - 若
errors为空:调用onValid(fieldValues, e),异常被捕获后重抛; - 若有错误:调用
onInvalid(errors, e),并通过_focusError()自动聚焦第一个错误字段(对应默认选项shouldFocusError: true); - 最后通知订阅者更新
isSubmitted、isSubmitting、isSubmitSuccessful、submitCount等状态。
因此handleSubmit天然是异步安全的,且表单状态(提交次数、提交中、是否提交成功)全部自动维护。
七、Schema 校验:Yup / Joi / Superstruct / Zod
中文 README 明确指出库支持Yup、Joi、Superstruct 或自定义 Schema;新版特性清单还扩展了Zod、AJV。这些解析器通过resolvers生态(仓库内 src/types/resolvers.ts 定义了Resolver类型)以统一接口接入useForm的resolver选项:
import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from 'yup'; const schema = yup.object().shape({ firstName: yup.string().required(), age: yup.number().positive().integer().required(), }); function App() { const { register, handleSubmit, formState: { errors } } = useForm({ resolver: yupResolver(schema), }); return ( <form onSubmit={handleSubmit((d) => console.log(d))}> <input {...register('firstName')} /> <p>{errors.firstName?.message}</p> <input {...register('age')} /> <p>{errors.age?.message}</p> <input type="submit" /> </form> ); }原理上,resolver的返回值({ values, errors })会在提交或trigger时经_runSchema/_executeSchema路径合并进_formState.errors(见 src/logic/createFormControl.ts)。若无需 Schema 校验,不传resolver即可,内置规则完全够用——这也是文档强调“遵循 HTML 标准校验”的另一层含义。
八、与 UI 库集成:Controller
非受控架构下,MUI、Ant Design、React Native 等自带状态的组件无法直接用register的ref,此时使用Controller:
import { Controller, useForm } from 'react-hook-form'; import { Input } from 'some-ui-library'; function App() { const { control, handleSubmit } = useForm(); const onSubmit = (data) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <Controller name="firstName" control={control} render={({ field }) => <Input {...field} />} /> <input type="submit" /> </form> ); }Controller通过render属性把{ field, fieldState, formState }交给自定义组件,field内含value、onChange、onBlur、name与ref。其实现位于 src/controller.tsx,配套的useController可在自定义 Hook 中复用同一套桥接逻辑(src/useController.ts)。仓库测试 src/tests/controller.test.tsx 覆盖了 Controller 的渲染与交互行为。
九、进阶 API 速览
useForm还返回丰富的方法与状态,常用清单如下(完整类型见 src/types):
| API | 作用 | 参考实现/示例 |
|---|---|---|
watch(name?) | 订阅字段值变化,支持嵌套路径与数组 | examples/V7 示例 中watch相关文件 |
getValues() | 读取当前表单值 | src/logic/createFormControl.ts |
setValue(name, value) | 程序化设置字段值 | examples/V7/setValue.tsx |
reset(values?) | 重置表单 | examples/V7/resetForm.tsx |
trigger(name?) | 手动触发校验 | examples/V7/triggerFieldValidation.tsx |
setError / clearErrors | 手动设置/清除错误 | examples/V7 |
setFocus(name) | 聚焦指定字段 | examples/V7 |
useFieldArray | 动态增删表单数组项 | examples/V7/FieldArray.tsx |
useFormContext | 深层组件共享表单实例 | examples/V7/formProvider.tsx |
useWatch | 组件级按需订阅,避免整表单重渲染 | src/useWatch.ts |
其中useFieldArray的实现位于 src/useFieldArray.ts,提供append、prepend、insert、remove、swap、move、update、replace等数组操作;仓库在 src/tests/useFieldArray 下为每个操作都配有独立测试(如 append.test.tsx、move.test.tsx)。
十、总结与延伸阅读
React Hook Form 以「非受控 + Hooks + 按需订阅」的组合,在保证开发体验的同时兼顾性能与极小体积,并天然兼容 HTML 原生校验、主流 Schema 库与 UI 组件库,可同时用于 Web 与 React Native。
本文内容对应的仓库位置:
- 中文文档原文:docs/README.zh-CN.md
- v6 版本文档:docs/README.V6.md;v7 中文说明:docs/README.V7.zh-CN.md
- 核心实现:src/useForm.ts、src/logic/createFormControl.ts、src/constants.ts
- 可运行示例:examples/V7(v7 系列)、examples/V6(v6 系列)
- 演示应用与端到端测试:app/src、e2e
本文涉及版本、API 写法均以本仓库当前内容为准:v7 写法使用
{...register('name', rules)}与formState.errors,v6 写法使用ref={register}与顶层errors,请按实际安装版本选择对应 API。官方最新的 API 文档、FAQ 与 Form Builder 工具可在 react-hook-form.com 官网查阅。
【免费下载链接】react-hook-form📋 React Hooks for form state management and validation (Web + React Native)项目地址: https://gitcode.com/gh_mirrors/re/react-hook-form
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考