React Hook Form —— useForm 和 FormProvider+useFormContext

🎯 ​​useForm:表单管理的核心​

useForm是 React Hook Form 的核心 Hook,它提供了一系列方法和状态来管理整个表单。

  1. ​基本用法与返回值​
属性/方法 描述 常见使用场景或示例
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对象显示错误信息,还可以使用 setErrorclearErrors方法以编程方式管理错误。
  1. ​配置选项​​:

    在调用 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:跨组件共享表单状态​

当你的表单结构复杂,包含深层嵌套的字段时,逐层传递 registerformState会非常繁琐。FormProvideruseFormContext正是为了解决这个问题。

  1. ​FormProvider​​:

    FormProvider是一个上下文(Context)提供者组件。它接受 useForm返回的所有方法和管理对象(通常命名为 methods),并将其提供给子组件树中的任何组件。

  2. ​useFormContext​​:

    FormProvider包裹下的任何子组件中,你都可以使用 useFormContextHook 来直接访问父级表单的方法和状态,而无需通过 props 钻取(prop drilling)。

​常见问题与解决方案​​:

有时可能会遇到在子组件中注册了字段,但值没有正确传递到表单状态的情况。这通常是因为 ​​没有正确使用 FormProvider​ 包裹组件树。请确保:

  • 父组件通过 useForm()创建表单方法。
  • 使用 <FormProvider {...methods}>包裹整个表单。
  • 确保子组件在 FormProvider的层级之下。

📊 ​​useForm 与 FormProvider 核心功能对比​

特性 useForm FormProvider / useFormContext
​作用​ 创建表单实例,管理核心状态和方法 跨组件层级共享表单状态和方法
​使用场景​ 表单的根组件或主要表单管理组件 深层嵌套的表单字段组件
​数据流​ 直接获取和管理表单数据 通过上下文(Context)消费表单数据
​性能影响​ 创建表单实例,影响范围较大 优化props钻取,避免不必要的传递

🔄 ​​组合使用:处理复杂表单的最佳实践​

在实际项目中,常常组合使用 useFormFormProvider来构建可维护的复杂表单。

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;
相关推荐
小p6 小时前
react学习3: 闭包陷阱
前端·react.js
该用户已不存在6 小时前
Vibe Coding 入门指南:从想法到产品的完整路径
前端·人工智能·后端
Pedro6 小时前
Flutter - 日志不再裸奔:pd_log 让打印有型、写盘有序
前端·flutter
申阳6 小时前
Day 3:01. 基于Nuxt开发个人呢博客项目-初始化项目
前端·后端·程序员
三小河6 小时前
解决 React + SSE 流式输出卡顿:Nginx 关键配置实战
前端·架构·前端框架
玖月晴空6 小时前
Uniapp 速查文档
前端·微信小程序·uni-app
琉-璃6 小时前
vue3+ts 任意组件间的通信 mitt的使用
前端·javascript·vue.js
FogLetter7 小时前
React Fiber 机制:让渲染变得“有礼貌”的魔法
前端·react.js
不想说话的麋鹿7 小时前
「项目前言」从配置程序员到动手造轮子:我用Vue3+NestJS复刻低代码平台的初衷
前端·程序员·全栈