React Hook Form 的性能
性能是创建此库的主要原因之一。React Hook Form 依赖于不受控表单,这就是 register
函数捕获 ref
,而受控组件则使用 Controller
或 useController
来限制其重新渲染范围的原因。这种方法减少了用户在输入中键入或表单值在表单或应用程序根部发生更改时发生的重新渲染次数。与受控组件相比,组件挂载到页面的速度更快,因为它们占用的开销更少。作为参考,你可以参考 此仓库链接 中的快速比较测试。
如何创建可访问的输入错误和消息?
React Hook Form 基于不受控组件,这使你能够轻松构建可访问的自定义表单。(有关不受控组件的更多信息,请阅读 组件之间共享状态)
import React from "react"import { useForm } from "react-hook-form"export default function App() {const {register,handleSubmit,formState: { errors },} = useForm()const onSubmit = (data) => console.log(data)return (<form onSubmit={handleSubmit(onSubmit)}><label htmlFor="firstName">First name</label><inputid="firstName"aria-invalid={errors.firstName ? "true" : "false"}{...register("firstName", { required: true })}/>{errors.firstName && <span role="alert">This field is required</span>}<input type="submit" /></form>)}
它适用于类组件吗?
不,默认情况下不行。如果你想这样做,你可以构建一个包装器并将其用于你的类组件中。
你不能在类组件内使用 Hooks,但你可以在单个树中混合使用类和使用 Hooks 的函数组件。组件是使用类还是使用 Hooks 的函数,这仅仅是该组件的实现细节。从长远来看,我们希望 Hooks 成为人们编写 React 组件的主要方式。
如何重置表单?
有两种方法可以清除表单
-
HTMLFormElement.reset()
此方法与单击表单的重置按钮的效果相同。它只清除
input/select/checkbox
值。 -
React Hook Form API:
reset()
React Hook Form 的
reset
方法将重置所有字段值,并将清除表单中的所有errors
。
如何初始化表单值?
由于 React Hook Form 依赖于不受控表单,因此你可以为单个字段指定 defaultValue
或 defaultChecked
。但是,更常见且推荐的做法是通过将 defaultValues
传递给 useForm
来初始化表单。
function App() {const { register, handleSubmit } = useForm({defaultValues: {firstName: "bill",lastName: "luo",},})return (<form onSubmit={handleSubmit((data) => console.log(data))}><input {...register("firstName")} /><input {...register("lastName")} /><button type="submit">Submit</button></form>)}
对于异步默认值,你可以使用以下方法
-
异步
defaultValues
function App() {const { register, handleSubmit } = useForm({defaultValues: async () => {const response = await fetch("/api")return await response.json() // return { firstName: '', lastName: '' }},})} -
响应式
values
function App() {const { data } = useQuery() // data returns { firstName: '', lastName: '' }const { register, handleSubmit } = useForm({values: data,resetOptions: {keepDirtyValues: true, // keep dirty fields unchanged, but update defaultValues},})}
如何共享 ref 的使用?
React Hook Form 需要一个 ref
来收集输入值。但是,你可能想将 ref
用于其他目的(例如,滚动到视图或聚焦)。
import { useRef, useImperativeHandle } from "react"import { useForm } from "react-hook-form"type Inputs = {firstName: stringlastName: string}export default function App() {const { register, handleSubmit } = useForm<Inputs>()const firstNameRef = useRef<HTMLInputElement>(null)const onSubmit = (data: Inputs) => console.log(data)const { ref, ...rest } = register("firstName")const onClick = () => {firstNameRef.current!.value = ""}useImperativeHandle(ref, () => firstNameRef.current)return (<form onSubmit={handleSubmit(onSubmit)}><input {...rest} ref={firstNameRef} /><button type="button" onClick={onClick}>clear</button><button>Submit</button></form>)}
如果你无法访问 ref 会怎样?
实际上,你可以在没有 ref
的情况下 register
一个输入。实际上,你可以手动 setValue
、setError
和 trigger
。
注意:由于 ref
未注册,React Hook Form 将无法向输入注册事件监听器。这意味着你必须手动更新值和错误。
import React, { useEffect } from "react"import { useForm } from "react-hook-form"export default function App() {const { register, handleSubmit, setValue, setError } = useForm()const onSubmit = (data) => console.log(data)useEffect(() => {register("firstName", { required: true })register("lastName")}, [])return (<form onSubmit={handleSubmit(onSubmit)}><inputname="firstName"onChange={(e) => setValue("firstName", e.target.value)}/><inputname="lastName"onChange={(e) => {const value = e.target.valueif (value === "test") {setError("lastName", "notMatch")} else {setValue("lastName", e.target.value)}}}/><button>Submit</button></form>)}
为什么第一次按键不起作用?
确保你没有使用 value
。正确的属性是 defaultValue
。
React Hook Form 专注于不受控输入,这意味着你无需通过 state
或 onChange
来更改输入 value
。实际上,你根本不需要 value
。你只需要设置 defaultValue
来指定初始输入值。
import { useForm } from "react-hook-form/dist/index.ie11" // V6import { useForm } from "react-hook-form/dist/react-hook-form.ie11" // V5'// Resolversimport { yupResolver } from "@hookform/resolvers/dist/ie11/yup"
React Hook Form、Formik 或 Redux Form?
首先,所有这些库都试图解决同一个问题:尽可能地简化表单构建体验。但是,这三个库之间存在一些基本差异。react-hook-form
是针对不受控输入而构建的,并试图为你的表单提供最佳的性能和最少的重新渲染次数。此外,react-hook-form
是使用 React Hooks 构建的,并用作一个 Hook,这意味着你没有要导入的组件。以下是一些详细的差异
React Hook Form | Formik | Redux Form | |
---|---|---|---|
组件 | 不受控 和 受控 | 受控 | 受控 |
渲染 | 最小程度的重新渲染和优化计算 | 根据本地状态更改重新渲染(在你在输入中键入时。) | 根据状态管理库(Redux)更改重新渲染(在你在输入中键入时。) |
API | Hooks | 组件(RenderProps、Form、Field)+ Hooks | 组件(RenderProps、Form、Field) |
包大小 | 小[email protected] 8.5KB | 中等[email protected] 15KB | 大[email protected] 26.4KB |
验证 | 内置的、Yup、Zod、Joi、Superstruct,以及你自己的验证。 | 自己构建或 Yup | 自己构建或插件 |
学习曲线 | 低到中等 | 中等 | 中等 |
watch vs getValues vs state
- watch:通过事件监听器订阅所有输入或指定输入的更改,并根据订阅的字段重新渲染。查看 此代码沙箱 以了解实际行为。
- getValues:获取存储在自定义 Hook 中的作为引用存储的值,速度快且开销小。此方法不会触发重新渲染。
- 本地状态:React 本地状态不仅仅代表输入的状态,还决定要渲染的内容。这将在每个输入更改时触发。
为什么默认值使用三元运算符时无法正确更改?
React Hook Form 不会控制你的整个表单和输入,这就是 React 无法识别实际输入已被交换或替换的原因。作为解决方案,你可以通过为输入提供唯一的 key
属性来解决此问题。你还可以从 Kent C. Dodds 撰写的这篇文章 中了解有关 key 属性的更多信息。
import React from "react"import { useForm } from "react-hook-form"export default function App() {const { register } = useForm()return (<div>{watchChecked ? (<input {...register("input3")} key="key1" defaultValue="1" />) : (<input {...register("input4")} key="key2" defaultValue="2" />)}</div>)}
如何使用模态框或选项卡表单?
重要的是要理解,React Hook Form 通过将输入状态存储在每个输入中(除了在 useEffect
中自定义 register
之外)来拥抱原生表单行为。一个常见的误解是输入状态会随着已挂载或已卸载输入而保留。例如,在使用模态框或选项卡表单时。相反,正确的解决方案是为每个模态框或选项卡内的表单构建一个新的表单,并在本地或全局状态中捕获你的提交数据,然后对组合数据进行操作。
或者,你可以在调用 useForm
时使用已弃用的选项 shouldUnregister: false
。
import { useForm, Controller } from "react-hook-form"function App() {const { control } = useForm()return (<Controllerrender={({ field }) => <input {...field} />}name="firstName"control={control}defaultValue=""/>)}
感谢你的支持
如果你发现 React Hook Form 在你的项目中很有用,请考虑为其加星并支持它。