React Hook Form使用指南

· 4 min read

表单作为用户交互中常见的形式,值的校验/收集/管理是个问题。

在react技术栈下中React Hook Form包名: react-hook-form功能强大,是个不错的方案。灵活恰当的使用它,可以提升code效率/可维护性。

这里就把hook form的常用场景/使用/容易忽略的点/核心原理做一个总结,如有疏漏,请斧正。

https://static.1991421.cn/2025/2025-03-31-142653.jpeg

当前版本:v7.56.3,压缩使用的包体积为11KB左右

使用🔨

内置方法

实际使用中,我们常用的只是useForm/Controller/getValues

抛开常用的useForm/useWatch/Controller,hook-form还有其他一些方法,这里就介绍下。

useForm中的mode

mode不同,影响表单收集错误的时机,但是本身对于formState,比如isValid,又比如单个字段下的 validate,是并不影响的,都是会实时更新。

mode缺省值是onSubmit

useFormContext

hook,很多时候,我们管理form是使用的useForm,如果表单组件是嵌套的,那么嵌套的表单项的值管理就成了问题。常常需要将父组件的control传递给子组件,然后子组件再传递给孙组件,这样一层一层传递,非常麻烦。如果是使用useFormContext,那么就可以直接在组件内部管理form的值,而不需要一层一层传递。

useFieldArray

hook,有时数据存在数组/List类型。比如动态添加项,使用useFieldArray会更为方便。

  const { fields, append, prepend, remove, swap, move, insert } = useFieldArray(
    {
      control, // control props comes from useForm (optional: if you are using FormProvider)
      name: 'test' // unique name for your Field Array
    }
  );

watch

watchfunc,useWatch用来监听关心的字段变化,或者所有字段当前的值,但假如想知道每次变化哪个字段,或者批量watch用于逻辑处理的话,可以使用watch方法。

  const allFieldWatch = useWatch({
    control,
    name:['price'], // 如果name不传,则是watch所有字段
  })
useEffect(() => {
    const wFn = watch((data, {name}) => {
      console.log('column changed', data, name) // data为修改后最新值
    });
    return wFn.unsubscribe;
  }, [])

registers

registerfunc,注册一个表单字段,返回对象,该方法主要是面向原生表单元素的,如果是非原生表单元素,比如Tea组件下的Input组件,注意onChange等方法是否会有问题。

              <input
                {...register('address', {
                  validate: (s) => {
                    console.log('address validate', s);
                  }
                })}
              />

register vs Controller

如上所说,register适合原生,Controller适合高阶表单组件,如第三方。

resetfunc/ setValue

  1. reset方法如果没有声明字段值,则会重置为defaultValues设置的值。

    
    reset();
    
  2. reset操作是面向所有字段进行重置,并非部分字段。比如reset只标注了A字段,那么其它字段则会是undefined.

  3. 如果想reset单个字段,应该使用setValue法或者reset时候携带所有字段值

    
     reset({
     ...getValues(),
     price: 111
    })
    

valueAsNumber/valueAsDate

              <input
                {...register('quantity', {
                  // valueAsNumber: true,
                })}
              />


//  "quantity": "2121212121"
//  "quantity": 2121212121

如果是Controller方式,需要自行实现,比如field.onChange(Number(value))

官方推荐-实践

transform/parse

react-hook-form介绍的transform和parse只是个实践,并不是内置的功能。

配套-yup等校验类库

const schema = yup
  .object()
  .shape({
    price: yup.number().required(),
    quantity: yup.number().min(1).max(100).required(),
  })
  .required();

const formProps = useForm({
    ...,
    resolver: yupResolver(schema),
});

注意:触发娇艳的实际还是取决于mode的设置。

dirty

常见问题❓

array数据更新,没有watch到

源码👀

包依赖

"peerDependencies": {
    "react": "^16.8.0 || ^17 || ^18 || ^19"
  },

延伸下。微信小程序开发框架taro是使用的react进行开发,那么如果需要表单校验的话,可以使用react-hook-form吗?

答案:No

补充信息

react-hook-form什么时候推出的

查询发现,react-hook-form于Mar 3, 2019发布的第一个版本,目前也属于高频维护。

其它form方案选择

![image-20250511225744979](/Users/alanhe/Library/Mobile Documents/comappleCloudDocs/Typora/image-20250511225744979.png)

useWatch原理

本身是在hook的effect中调用了form.control的subscribe方法,来监听表单值的变化,一旦值变动了,则会更新value对象。

export function useWatch<TFieldValues extends FieldValues>(
  props?: UseWatchProps<TFieldValues>,
) {
  ...
  React.useEffect(
    () =>
      control._subscribe({
        name: _name.current as InternalFieldName,
        ...
        callback: (formState) =>
          !disabled &&
          updateValue(
            generateWatchOutput(
              _name.current as InternalFieldName | InternalFieldName[],
              control._names,
              formState.values || control._formValues,
              false,
              _defaultValue.current,
            ),
          ),
      }),
    [control, disabled, exact],
  );

  const [value, updateValue] = React.useState(
    control._getWatch(
      name as InternalFieldName,
      defaultValue as DeepPartialSkipArrayKey<TFieldValues>,
    ),
  );
...
  return value;
}

form mode

相关链接