18 - Get Field Values

watch不同,getValues方法不会触发页面rerender,也不订阅input changes。

当用户点击按钮或者做特定action时,使用getValues方法是比watch更好的选择。

const { getValues } = useForm()

image-20251027160737462

1、获取全部form values

可以看到,input的change事件并不会触发getValues事件。

2、获取一个form value

 

3、获取多个form values

19 - Set Field Value

setValue方法可以让我们设置已经registered的filedvalue,通过程序的方法来设置。

image-20251027162237353

可以看到,username的值变为空:

设置值成功了,但是devtools里面的touched这些都没有变化,说明此时react hook form是没有把它当作用户交互的,里面可以配置。配置了之后,就可以给出提示,看设置值是否符合需求。

再来触发,就可以看到devtools里面变化了。

20 - Touched and Dirty States

devtools里面的Touched,是一个boolean值,表示用户是否交互过。Dirty是一个boolean值,表示用户是否修改过值(是与default value来比较,值是否修改过)。

可以从useForm里面的formState解构出整个form的touched和dirty。

可以看到,输出的值是对象,里面表示哪些field交互或者修改了。

另外formState里面还有一个isDirty属性,用来表示整个form是否被修改过。

可以看到,如果form中的任一项被修改了,isDirty都会变为true。

isDirty可以用于enable submit button,只有当用户修改了数据之后,才能点击按钮。

21 - Disabling Fields

传统写法,如果要设置disable一个input,那么需要在input上设置它的disabled属性。

在react hook form中,通过设置register的第二个参数中,添加disabled: true来disable一个input。

需要注意的是,当使用react hook form的方式diasble一个input之后,这个input的值会变为undefined,而且它的校验也失效了。

上面的代码里面,设置了disabled,也设置了required,那么required会生效吗?填写表单里面的其它信息,提交表单看一下。

可以看到,不会出现error信息,输出的social里面也没有twitter,说明twitter此时的值是undefined。

这是react hook form的现象,记住有这种现象即可,一般我都不会把一个必填项设置为静态的disabled,一般来说都是根据用户的输入来决定。比如说这里设置,当channel字段为空时,disabled才为true。

22 - Handle Submission Error

之前的案例中,在form的onSubmit上面,绑定的是handleSubmit(onSubmit),其中handleSubmit是从useForm解构出来的,onSubmit是自己定义的。那么为什么不直接绑定onSubmit呢?因为我看到handleSubmit也没有做额外的事情啊。

这是因为handleSubmit除了可以处理提交之外,还可以处理提交遇到的错误。

image-20251027172809177

效果:

 

这个submission error看上去不怎么样,其实我觉得逻辑非常清晰,这里就是专门用来获取validation相关的报错信息的,和接口请求是分开的,如果这里有报错,那么handleSubmit的第一个参数onSubmit就不会执行,逻辑很清除。我之前在vue里面都是写成一大堆,逻辑都很杂乱。

使用这个submission error,可以获取到message信息,这样就可以使用messagebox的方式来提示用户。

23 - Disable Form Submission

有时候如果表单没有填写一些必填项,那么我想让submit button处于disabled的状态,这时候就可以使用watch来监听,<button disabled={watch("username") === "" || watch("email") === ""}>Submit</button>

如果是要用户修改表单任意一项之后,submit button才能点击,那么可以使用isDirty来判断。

可以看到,当表单任意一项更改之后,submit button才能点击。

还有isValid可以用来监听form是否通过校验,只有通过校验之后才能点击。

这里我有一个疑问,就是之前说过,设置的校验规则只能在submit时触发,如果是第一次填写表单,填写完成了,都符合要求了,这时候的submit button还是disabled状态吗?

这取决于isValid此时是什么值,可以看一下下面的动图,从useForm获取到的formState的相关值都是实时更新的,所以上面的问题是不成立的。如果都填好了,都符合要求了,那么此时的isValidtrue

24 - Form Submission State

formState里面有很多状态,前面学习了errorstouchedFieldsdirtyFieldsisDirtyisValid。这节课来学习form提交相关的状态。

全部状态可以查看官方文档:https://react-hook-form.com/docs/useform/formstate

image-20251027200430929

这几个状态的含义如下:

image-20251027200754459

输出看一下:

其中有用的就是isSubmitting,可以加到submit button上面去,避免用户多次点击。

25 - Reset Form

当用户填写完成之后、或者用户刚进入页面之后,我们需要将form设置成初始状态,这时候可以使用useForm提供的reset。reset方法是重置整个表单。

将表单填满后,点击reset:

 

一般不通过点击按钮的方式来reset,而是使用useEffect

表单填好之后,点击提交,看是否会清空表单:

可以看到,提交成功之后,就触发了reset。

26 - Async Validation

案例:在用户输入email时,校验是否存在同样的email地址,使用接口https://jsonplaceholder.typicode.com/users?email=Sincere@april.biz来校验,这时候校验就要等到接口数据返回之后才能继续,所以这里需要使用async校验。

image-20251027204050206

需要在email的register里面,新增一个校验函数。

可以看到,在校验通过之前,是不会提交的。

这个会不会造成性能问题,就是用户输入一点,就校验一次?在emailAvailable里面输出信息看一下console.log("email 查询了")

确实会造成性能问题,所以这个校验还不能放在这里,看有没有更好的办法。

 

问了一下chatgpt,可以使用防抖来解决这个问题。也可以在提交的时候调用校验,如果有问题,就使用setError方法来给出提示信息。

27 - Validation Modes

这节课学习react hook form的校验模式,默认的校验模式是用户点击submit button之后触发校验。useForm有一个参数mode,通过它可以修改校验模式。

image-20251028085340722

mode有5种值,如下:

image-20251028085440222

注意:使用onChange模式时,会引起多次rerender,从而影响性能。all模式就更不用说了,加上了onBluronChange,性能肯定更差。

下面以onBlur模式来试一下效果:

onBlur模式其实很好,因为有isValid可以作为submit button的判断依据,所以如果校验没有通过,submit button是可以disabled,避免用户点击。

28 - Manually Trigger Validations

这节课学习手动触发校验。

useForm提供了trigger方法,可以触发单个、多个input的校验,也可以触发整个form的校验。需要注意的是下面图中的rules说明了:react hook form的隔离渲染优化仅仅适用于手动触发单个input的校验,如果是多个input校验或者整个form校验,则会引整个formState的重新渲染。

image-20251028090624884

1、手动触发整个form校验

看一下效果:

2、触发单个field校验

 

3、触发多个field校验

 

学习的react hook form更深入的内容,就是下面这些:

image-20251028091151853

29 - Yup Integration

虽然react hook form本身已经很好了,但是还是有一些优化的地方。比如说在校验的时候,能不能写的更简单一点。

Yup没有自己的官网,可以到它的github里面查看文档:https://github.com/jquense/yup?tab=readme-ov-file

Yup is a schema builder for runtime value parsing and validation. Define a schema, transform a value to match, assert the shape of an existing value, or both. Yup schema are extremely expressive and allow modeling complex, interdependent validations, or value transformation.

Yup可以结合react hook form一起使用,先定义schema,然后校验数据。

创建文件mycode\rhf-demo\src\components\YupYoutubeForm.tsx,编写简单的表单。

image-20251028100408303

安装依赖npm i yup @hookform/resolvers,这里的@hookform/resolvers是连接react hook form和yup的桥梁。

引入依赖:

定义yup validation schema:

将schema和react hook form连接起来,定义userForm里面的resolver参数:

连接起来之后,react hook form就会按照schema定义的规则进行校验,并生成errors信息。我下面粘贴上完整的代码:

查看效果:

感觉代码会简单很多。

30 - Zod Integration

zod与yup类似,都是用于和react hook form结合使用,用来校验表单的。

https://zod.dev/

Zod is a TypeScript-first validation library. Using Zod, you can define schemas you can use to validate data, from a simple string to a complex nested object.

创建mycode\rhf-demo\src\components\ZodYoutubeForm.tsx,编写简单表单。

安装依赖npm i zod @hookform/resolvers

引入依赖:

定义schema:

将schema和react hook form结合起来:

完整代码:

查看效果:

之前看zod视频、文档,之所以看不懂,是因为我看到明明已经定义了数据的typescript类型,为什么在schema里面还要定义一遍?

学习了之后才发现,zod在react hook form中是专门用来校验的,这一点理解清楚了就好做了。

31 - Material UI Integration

这节课学习怎么将mui结合react hook form一起使用。使用mui components创建一个login form。

安装依赖npm install @mui/material @emotion/react @emotion/styled

创建mycode\rhf-demo\src\components\MuiLoginForm.tsx,编写简单的表单。

结合react hook form进行编写:

需要注意的就是errors的展示方法,mui的组件内置了属性,可以直接写在上面。

注释掉App.tsxmain.tsx里面引入的css文件,查看效果:

额外话:Controller

老师没有讲解Controller,但是我看到很多人使用react hook form的时候,都用到了Controller,难道真的需要学习这个吗?

应该是需要的,因为react hook form提倡使用非受控组件和原生HTML表单输入,如果使用的是第三方UI库提供的组件,那么这些组件一般都是受控组件,此时就需要使用Controller来更好的控制这些UI组件。

问了一下chatgpt。

React Hook Form (RHF) 中,Controller 是一个非常核心的组件,用来在 React Hook Form 和第三方 UI 组件(例如 MUI、Ant Design、React-Select 等)之间建立连接


🧩 一句话解释

Controller 是一个“中间层”,让那些不是原生表单元素(如 <input><select>)的自定义组件也能受 React Hook Form 控制。


🔍 为什么需要 Controller

React Hook Form 对原生的表单元素(input, select, textarea)支持很好,可以直接用 register() 来注册:

但是像这样第三方 UI 组件就不行了 👇

这些组件不一定触发标准的 onChangeonBlur 事件,也不一定用 value 属性来表示当前值。 所以你不能简单地用 register()

这时就需要 Controller手动指定


⚙️ 基本用法


🧠 Controller 的核心属性

属性作用
name字段名(必须)。用我的理解就是定义的变量名
control来自 useForm() 的控制对象
rules验证规则(可选)
defaultValue默认值(建议在 useForm 里统一设置)
render一个 render prop,用来渲染表单组件

render 回调会收到一个对象,常用属性如下:


🧩 举例:配合 react-select


🧭 总结

情况用法
原生 <input> / <select>直接用 register()
第三方 / 自定义组件Controller 包起来
想统一控制验证、值和状态Controller 是首选

创建一个mycode\rhf-demo\src\components\ControllerForm.tsx,编写简单的组件:

查看效果:

 

这个案例里面有一个报错,记录一下。当使用coerce将input值强制转换了,但是使用z.infer推导类型的时候,会将类型推导为unknown。所以会报这种错误。

image-20251028204734192

官网里面有解答,如下:

https://zod.dev/api?id=coercion

image-20251028210322428

那么代码改成这样就行了:

image-20251028210402247