🎯 useForm:表单管理的核心
useForm是 React Hook Form 的核心 Hook,它提供了一系列方法和状态来管理整个表单。
- 基本用法与返回值:
| 属性/方法 | 描述 | 常见使用场景或示例 |
|---|---|---|
register |
注册表单字段,接受字段名和验证规则 | {...register('username', { required: true })} |
handleSubmit |
处理表单提交,接收成功验证后的回调函数 | <form onSubmit={handleSubmit(onSubmit)}> |
formState |
包含表单状态信息的对象 | 详见下方 formState详解 |
watch |
观察指定表单字段或所有字段的值的变化,返回当前值 | const username = watch('username') |
reset |
重置表单字段的值到默认状态,可指定新值 | reset({ username: '', email: '' }) |
setValue |
手动设置表单中某个字段的值 | setValue('username', 'newValue') |
setError |
手动设置字段的错误信息 | setError('username', { type: 'manual', message: 'Error msg' }) |
clearErrors |
清除所有或指定字段的错误信息 | clearErrors('username') |
control |
用于控制表单数据的对象,在与 Controller组件或 useController配合时必需 |
<Controller name="test" control={control} render={...} /> |
trigger |
手动触发表单字段的验证 | trigger('username') |
💡 详解 formState
formState是一个包含表单状态信息的对象,常用的属性包括:
errors: 包含所有字段验证错误信息的对象。你可以通过errors.username?.message的方式获取特定字段的错误信息。isDirty: 布尔值,表示用户是否修改过任何表单字段。isValid: 布尔值,表示当前表单的所有字段是否都通过验证。isSubmitting: 布尔值,表示表单是否正在提交(通常在handleSubmit的异步提交过程中为true)。isValidating: 布尔值,表示表单是否正在执行异步验证。touchedFields: 对象,记录哪些字段被用户交互过(如获取过焦点)。submitCount: 数字,记录表单尝试提交的次数。
🛠️ 使用示例
js
import { useForm } from 'react-hook-form';
function MyForm() {
const {
register,
handleSubmit,
formState: { errors, isDirty, isSubmitting },
watch,
reset,
setValue
} = useForm({
defaultValues: { // 初始化表单值
username: '',
email: ''
}
});
const onSubmit = (data) => { // 提交处理函数
console.log(data);
};
const watchedUsername = watch('username'); // 观察 username 字段
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register('username', {
required: 'Username is required',
minLength: { value: 3, message: 'Min length is 3' }
})}
/>
{errors.username && <p>{errors.username.message}</p>}
<input
type="email"
{...register('email', {
required: 'Email is required',
pattern: {
value: /^\S+@\S+$/i,
message: 'Invalid email address'
}
})}
/>
{errors.email && <p>{errors.email.message}</p>}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
<button type="button" onClick={() => reset({ username: '', email: '' })}>
Reset
</button>
</form>
);
}
⚠️ 使用时注意
- 值的类型 :
useForm返回的表单值默认都是字符串类型 。如果需要数字、布尔值等类型,需手动转换(例如使用Number())或在提交时处理。 - 性能优化 :
React Hook Form通过非受控组件和引用的方式管理表单,避免了每次输入都重新渲染组件,性能较好。watch方法默认会触发组件的重新渲染,如需观察值但不希望引起重渲染,可考虑使用useWatch。 - 错误处理 :除了通过
errors对象显示错误信息,还可以使用setError和clearErrors方法以编程方式管理错误。
-
配置选项:
在调用
useForm时,你可以传入一个配置对象来定制表单行为。
| 参数名 | 类型 | 可选值/类型 | 描述 |
|---|---|---|---|
mode |
string | 'onSubmit'(默认)、'onBlur'、'onChange'、'onTouched'、'all' |
设置表单验证的触发时机。例如,'onChange'会在每次输入变化时进行验证,'onBlur'会在字段失去焦点时验证,而 'onSubmit'只在提交时验证。 |
defaultValues |
object | 例如 { fieldName: initialValue } |
用于设置表单字段的初始值。此对象应包含表单中所有字段的键及其对应的初始值,在表单初始化时填充。 |
resolver |
function | 例如 zodResolver(schema), yupResolver(schema) |
用于集成像 Zod 、Yup 这样的第三方验证库,以实现更复杂、声明式的验证逻辑。需要配合 @hookform/resolvers使用。 |
js
import { useForm } from "react-hook-form";
export default function MyForm() {
const {
register,
handleSubmit,
watch,
reset,
setValue,
getValues,
control,
trigger,
setError,
clearErrors,
unregister,
formState: { errors, isDirty, isSubmitting, isValid }
} = useForm({
defaultValues: { username: "", email: "" },
mode: "onChange", // 校验触发时机
});
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("username", { required: "用户名必填" })} />
{errors.username && <p>{errors.username.message}</p>}
<button type="submit" disabled={!isValid || isSubmitting}>提交</button>
</form>
);
}
🧩 FormProvider 与 useFormContext:跨组件共享表单状态
当你的表单结构复杂,包含深层嵌套的字段时,逐层传递 register或 formState会非常繁琐。FormProvider和 useFormContext正是为了解决这个问题。
-
FormProvider:
FormProvider是一个上下文(Context)提供者组件。它接受useForm返回的所有方法和管理对象(通常命名为methods),并将其提供给子组件树中的任何组件。 -
useFormContext:
在
FormProvider包裹下的任何子组件中,你都可以使用useFormContextHook 来直接访问父级表单的方法和状态,而无需通过 props 钻取(prop drilling)。
常见问题与解决方案:
有时可能会遇到在子组件中注册了字段,但值没有正确传递到表单状态的情况。这通常是因为 没有正确使用 FormProvider 包裹组件树。请确保:
- 父组件通过
useForm()创建表单方法。 - 使用
<FormProvider {...methods}>包裹整个表单。 - 确保子组件在
FormProvider的层级之下。
📊 useForm 与 FormProvider 核心功能对比
| 特性 | useForm | FormProvider / useFormContext |
|---|---|---|
| 作用 | 创建表单实例,管理核心状态和方法 | 跨组件层级共享表单状态和方法 |
| 使用场景 | 表单的根组件或主要表单管理组件 | 深层嵌套的表单字段组件 |
| 数据流 | 直接获取和管理表单数据 | 通过上下文(Context)消费表单数据 |
| 性能影响 | 创建表单实例,影响范围较大 | 优化props钻取,避免不必要的传递 |
🔄 组合使用:处理复杂表单的最佳实践
在实际项目中,常常组合使用 useForm和 FormProvider来构建可维护的复杂表单。
js
import React from 'react';
import { useForm, FormProvider, useFormContext } from 'react-hook-form';
// 一个深层嵌套的输入字段组件
const DeepNestedInput = () => {
// 在子组件中直接获取表单上下文
const { register, formState: { errors } } = useFormContext();
return (
<div>
<input {...register("deepField", { required: "此字段必填" })} />
{errors.deepField && <p>{errors.deepField.message}</p>}
</div>
);
};
// 表单的根组件
const ComplexForm = () => {
// 在根组件创建表单方法
const methods = useForm({
defaultValues: {
deepField: ''
},
mode: 'onBlur'
});
const onSubmit = (data) => {
console.log('表单数据:', data);
};
// 用FormProvider包裹子组件
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
<DeepNestedInput />
<button type="submit">提交</button>
</form>
</FormProvider>
);
};
export default ComplexForm;