► React Hook Form V7 is released. If you are planning to upgrade, please read through the Migration Guide to V7.
useForm: Function
useForm
也接受可选参数。 下面的示例展示了所有选项的默认值。
const { register } = useForm({ mode: 'onSubmit', reValidateMode: 'onChange', defaultValues: {}, resolver: undefined, context: undefined, criteriaMode: "firstError", shouldFocusError: true, shouldUnregister: true, })
type FormInputs = { firstName: string; lastName: string; }; const { register } = useForm<FormInputs>({ mode: 'onSubmit', reValidateMode: 'onChange', defaultValues: {}, resolver: undefined, context: undefined, criteriaMode: "firstError", shouldFocusError: true, shouldUnregister: true, })
mode: onChange | onBlur | onSubmit | onTouched | all = 'onSubmit'
React Native: compatible with Controller
名字 | 类型 | 説明 |
---|---|---|
onSubmit (Default) | string | 校验将在submit 事件 上触发,无效的输入将附加onChange事件侦听器以重新校验。 |
onBlur | string | 校验将在blur 事件上触发。 |
onChange | string | 校验将在每个输入组件的change 事件上触发,会导致大量的重渲染。警告:这样通常会对性能造成影响。 |
onTouched | string | 直到输入被触动,才会触发验证。 |
all | string | 校验将在blur 和change 事件上触发。 |
reValidateMode: onChange | onBlur | onSubmit = 'onChange'
React Native: Custom register or using Controller
此选项允许您配置提交后有错误的输入组件重新校验的场景(默认情况下,在输入组件变化时触发校验)。 |
defaultValues: Record<string, any> = {}Video
您可以使用
defaultValue/defaultChecked
设置输入组件的默认值(从React文档中了解默认值的更多内容),或者将defaultValues
作为可选参数来填充整个表单的默认值。重要提示:
defaultValues
将在首次渲染时缓存在自定义hook中,要重置defaultValues
,请使用api。注意:
defaultValues
中定义的值将如defaultValue
一样注入。手动注册的输入组件不会自动填充(例如:
register('test')
),因为React Hook Form没有自定义register
表单项的ref
。它并不是表单的默认状态, 要包含其他的表单值,可以这样做:
注册隐藏的输入组件:
<input type="hidden" ref={register} name="test" />
在提交回调里组合所有的值.
const { register } = useForm({ defaultValues: { firstName: "bill", lastName: "luo", email: "bluebill1049@hotmail.com", isDeveloper: true } }) <input name="firstName" ref={register} /> // ✅ working version <input name="lastName" ref={() => register({ name: 'lastName' })} /> // ❌ above example does not work with "defaultValues" due to its "ref" not being provided
type Inputs = { firstName: string; lastName: string; email: string; isDeveloper: boolean; } const { register } = useForm<Inputs>({ defaultValues: { firstName: "bill", lastName: "luo", email: "bluebill1049@hotmail.com", isDeveloper: true } }) <input name="firstName" ref={register} /> // ✅ working version <input name="lastName" ref={() => register({ name: 'lastName' })} /> // ❌ above example does not work with "defaultValues" due to its "ref" not being provided
resolver: (values: any, context?: object) => Promise<ResolverResult> | ResolverResult
该函数允许使用外部校验库,例如Yup, Joi,Superstruct以及其他的。我们的目标是确保用户可以无缝集成任何喜欢的校验库。如果你并没有使用一个库,那也可以一直编写自己的逻辑来校验表单。
目前,官方支持了Yup, Joi and Superstruct的解析器。
npm install @hookform/resolvers
关于构建自定义解析器的说明:
确保返回的对象包含
values
和errors
属性,并且它们的默认值应为{}
。返回
error
对象的键应和表单项的name
值匹配。该函数将被缓存在的自定义hook中,而
context
则是每次重渲染都会变更的可变对象。用户交互时,输入组件只会有一个表单项发生重新校验。库本身会计算错误对象,并且触发相应的重渲染。
import React from 'react'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from "yup"; const schema = yup.object().shape({ name: yup.string().required(), age: yup.number().required(), }); const App = () => { const { register, handleSubmit } = useForm({ resolver: yupResolver(schema), }); return ( <form onSubmit={handleSubmit(d => console.log(d))}> <input {...register("name")} /> <input type="number" {...register("age")} /> <input type="submit" /> </form> ); };
import React from 'react'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from "yup"; type Inputs = { name: string; age: string; }; const schema = yup.object().shape({ name: yup.string().required(), age: yup.number().required(), }); const App = () => { const { register, handleSubmit } = useForm<Inputs>({ resolver: yupResolver(schema), // yup, joi and even your own. }); return ( <form onSubmit={handleSubmit(d => console.log(d))}> <input {...register("name")} /> <input type="number" {...register("age")} /> <input type="submit" /> </form> ); };
shouldUnregister: boolean = true
CodeSandbox
true | false | |
Can you unregister an input? | ✅ | ❌ |
Value remains when input unmounts? | ❌ | ✅ |
Is form state updated? eg: isValid, isDirty, touched | ✅ | ❌ you will need to clear manually |
object | 该上下文对象将被注入 |
firstError | all | 默认行为 设置为 |
boolean = true | 默认情况下,当用户提交表单并包含错误时, 具有错误的第一个字段将被聚焦。 注意: 只有具有 |
♦
register: (Ref, RegisterOptions?) => voidReact Native: Custom register or using Controller
该方法允许您将输入组件/选择组件中的Ref
和校验规则注册到 React Hook Form 中。
校验规则都是基于HTML标准,也允许自定义校验。
重要: name
是必需且唯一的(非数字起始)。 输入组件名称还支持点和方括号语法,它允许您轻松创建嵌套的表单项。
Input 名字 | 提交结果 |
---|---|
name="firstName" | { firstName: 'value'} |
name="name.firstName" | { name: { firstName: 'value' } } |
name="name.firstName[0]" | { name: { firstName: [ 'value' ] } } |
如果用在简单的表单项列表上,则可以将输入名称指定为name[index]
。 查看Field Array数组示例。 进阶用法可以查看 useFieldArray
.
自定义注册
您还可以手动注册输入组件,这在使用自定义组件和无法访问Ref
的情况下非常有用。在使用React Native或自定义组件(如react-select)时很常见。为了使用简单,我们提供了 组件来为您处理这个过程。
如果你决定不使用Controller
来手动注册表单项, 那么就需要使用 更新输入组件的值。
register('firstName', { required: true, min: 8 })
注意: 如果你想要自定义组件在值更新时触发一次重渲染,那么你应该为注册的输入组件提供一个类型。
register({ name: 'firstName', type: 'custom' }, { required: true, min: 8 })
注意: 如果多个单选框类型的输入组件都有一样的name,则需要在最后一个输入组件注册校验规则以使 hook 知道要将它们一起校验。
名字 | 説明 | 程式码范例 |
---|---|---|
ref React.RefObject | React element ref |
|
required boolean | 布尔项,true表示输入必须有值,才能提交表单。 您可以在errors 中赋值为字符串以返回错误消息。 |
|
maxLength
| 文本框能接受的最大字符数。 |
|
minLength
| 文本框能输入的最小字符数。 |
|
max
| 接受的最大值。 |
|
min
| 接受的最小值。 |
|
pattern
| 校验文本框内容的正则表达式。 |
|
validate Function | Object | 您可以把回调函数作为参数传给validate,或者传入值为回调函数的对象来逐个校验。(参考例子) |
|
valueAsNumber:boolean | Returns a Number normally. If something goes wrong Note: |
|
valueAsDate:boolean | Returns a Date normally. If something goes wrong Note: |
|
setValueAs:<T>(value: any) => T | Return input value by running through the function. Note: |
|
unregister: (name: string | string[]) => void
此方法允许您unregister
单个或一组输入组件。
注意: 取消注册输入时,它的值将不再被包含在被提交的表单数据中。
import React, { useEffect } from "react"; import { useForm } from "react-hook-form"; export default function App() { const { register, handleSubmit, unregister } = useForm(); const onSubmit = data => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="firstName" ref={register} /> <input name="lastName" ref={register} /> <button type="button" onClick={() => unregister("lastName")}>unregister</button> <input type="submit" /> </form> ); }
import React, { useEffect } from "react"; import { useForm } from "react-hook-form"; interface IFormInputs { firstName: string; lastName?: string; } export default function App() { const { register, handleSubmit, unregister } = useForm<IFormInputs>(); const onSubmit = (data: IFormInputs) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="firstName" ref={register} /> <input name="lastName" ref={register} /> <button type="button" onClick={() => unregister("lastName")}>unregister</button> <input type="submit" /> </form> ); };
errors: Record<string, Object>
包含每个输入组件表单错误和错误消息的对象。
名字 | 类型 | 説明 |
---|---|---|
type | string | Error 类型. eg: required, min, max, minLength |
types | Record<{ string, string | boolean }> | 在需要某个输入组件所有的校验错误时使用。比如;一个必填的密码框 同时限定了最小长度以及需要包含的特殊字符串 。 |
message | string | React.ReactElement | 默认情况下消息是空字符串。 但是,如果注册时配置了错误信息,那么它将被返回。 |
ref | React.Ref | 输入元素的引用。 |
注意: 避免使用对象键作为名称以致数据被覆盖。
eg: register('user'); register('user.type'); ❌ // error's type will get overwritten.
注意: 您可以使用 组件展示错误状态。
import React from "react"; import { useForm } from "react-hook-form"; export default function App() { const { register, errors, handleSubmit } = useForm(); const onSubmit = data => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("singleErrorInput", { required: true })} /> {errors.singleErrorInput && "Your input is required"} {/* refer to the type of error to display message accordingly */} <input {...register("multipleErrorInput", { required: true, maxLength: 50 })} /> {errors.multipleErrorInput?.type === "required" && "Your input is required"} {errors.multipleErrorInput?.type === "maxLength" && "Your input exceed maxLength"} {/* register with validation */} <input type="number" {...register("numberInput", { min: 50 })} /> {errors.numberInput && "Your input required to be more than 50"} {/* register with validation and error message */} <input {...register("errorMessage", { required: "This is required" })} /> {errors.errorMessage?.message} <input type="submit" /> </form> ); }
import * as React from 'react' import { useForm } from "react-hook-form"; interface IFormInputs { singleErrorInput: string multipleErrorInput: string numberInput: string } function App() { const { register, formState: { errors }, handleSubmit } = useForm<IFormInputs>(); const onSubmit = (data: IFormInputs) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <label>Error</label> <input {...register("singleErrorInput", { required: true })} /> {errors.singleErrorInput && <p>Your input is required</p>} <label>Error with type check</label> <input {...register("multipleErrorInput", { required: true, minLength: 5 })} /> {errors.multipleErrorInput?.type === "required" && ( <p>Your input is required</p> )} {errors.multipleErrorInput?.type === "minLength" && ( <p>Your input must be larger then 3 characters</p> )} <label>Error with value</label> <input type="number" {...register("numberInput", { min: 50 })} /> {errors.numberInput && <p>Your input required to be more than 50</p>} <input type="submit" /> </form> ); }
import React from "react"; import { useForm } from "react-hook-form"; type Inputs = { a: number; b: string; c: Date; d: { e: string; }; f: { g: number[]; h: string[]; i: { j: string }[]; }; k: any; l: any[]; m: unknown; n: unknown[]; o: object; p: object[]; q: { r: any; s: { t: any[]; u: unknown; v: object; }[]; w: Date[]; x: { y: { z: object[]; }; }; }; }; export default function App() { const { errors } = useForm<Inputs>(); console.log(errors?.a?.message); console.log(errors?.b?.message); console.log(errors?.c?.message); console.log(errors?.d?.e?.message); console.log(errors?.f?.g && errors.f.g[0] && errors.f.g[0].message ); console.log(errors?.f?.h && errors.f.h[0] && errors.f.h[0].message ); console.log( errors?.f?.i && errors?.f?.i[0] && errors.f.i[0].j && errors.f.i[0].j.message ); console.log(errors?.k?.message); console.log(errors?.l?.message); console.log(errors?.m?.message); console.log(errors?.n && errors.n[0] && errors.n[0].message); console.log(errors?.o?.message); console.log(errors?.p && errors.p[0] && errors.p[0].message); console.log(errors?.q?.r?.message); console.log( errors?.q?.s && errors.q.s[0] && errors.q.s[0].t.message ); console.log( errors?.q?.s && errors.q.s[0] && errors.q.s[0].u && errors.q.s[0].u.message ); console.log( errors?.q?.s && errors.q.s[0] && errors.q.s[0].v && errors.q.s[0].v.message ); console.log(errors?.q?.w && errors.q.w[0] && errors.q.w[0].message ); console.log( errors?.q?.x?.y?.z && errors.q.x.y.z[0] && errors.q.x.y.z[0].message ); return <form />; }
watch: (names?: string | string[]) => anyVideo
监听制定的表单项并返回他们的值,可用于监听决定重渲染的表单项。
未定义
defaultValue
时,watch
首次渲染会返回undefined
,因为它在register
之前被调用,但是您可以将defaultValue
设置为第二个参数。如果
defaultValues
在使用表单useForm
作为参数初始化,则首次渲染将返回defaultValues
中提供的内容。对于
useFieldArray
来说,当它包含的所有的表单项都被移除后,将会返回defaultValues
,可以通过查看fields.length
来确定这一点。
类型 | 説明 | 例子 | Return |
---|---|---|---|
string | 通过名称监听输入组件(类似于lodash的get函数) | watch('inputName') watch('inputName', 'value') | any |
string[] | 监听多个输入组件 | watch(['inputName1']) watch(['field1'], { field1: '1' }) | { [key:string] : any } |
undefined | 监听所有输入组件 | watch() watch(undefined, { field: '1' }) | { [key:string] : any } |
import React from "react"; import { useForm } from "react-hook-form"; function App() { const { register, watch, formState: { errors }, handleSubmit } = useForm(); const watchShowAge = watch("showAge", false); // you can supply default value as second argument const watchAllFields = watch(); // when pass nothing as argument, you are watching everything const watchFields = watch(["showAge", "number"]); // you can also target specific fields by their names // Callback version of watch. It's your responsibility to unsubscribe when done. React.useEffect(() => { const subscription = watch((value, { name, type }) => console.log(value, name, type)); return () => subscription.unsubscribe(); }, [watch]); const onSubmit = data => console.log(data); return ( <> <form onSubmit={handleSubmit(onSubmit)}> <input type="checkbox" {...register("showAge")} /> {/* based on yes selection to display Age Input*/} {watchShowAge && <input type="number" {...register("age", { min: 50 })} />} <input type="submit" /> </form> </> ); }
import React from "react"; import { useForm } from "react-hook-form"; interface IFormInputs { name: string showAge: boolean age: number } function App() { const { register, watch, formState: { errors }, handleSubmit } = useForm<IFormInputs>(); const watchShowAge = watch("showAge", false); // you can supply default value as second argument const watchAllFields = watch(); // when pass nothing as argument, you are watching everything const watchFields = watch(["showAge", "age"]); // you can also target specific fields by their names // Callback version of watch. It's your responsibility to unsubscribe when done. React.useEffect(() => { const subscription = watch((value, { name, type }) => console.log(value, name, type)); return () => subscription.unsubscribe(); }, [watch]); const onSubmit = (data: IFormInputs) => console.log(data); return ( <> <form onSubmit={handleSubmit(onSubmit)}> <input {...register("name", { required: true, maxLength: 50 })} /> <input type="checkbox" {...register("showAge")} /> {/* based on yes selection to display Age Input*/} {watchShowAge && ( <input type="number" {...register("age", { min: 50 })} /> )} <input type="submit" /> </form> </> ); }
import React from "react"; import { useForm } from "react-hook-form"; type Inputs = { key1: string; key2: number; key3: { key1: number; key2: boolean; }; }; export default function App(props) { const { watch } = useForm<FormValues>; watch(); // function watch(): FormValues watch({ nest: true }); // function watch(option: { nest: boolean; }): FormValues watch("key1"); // function watch<"key1">(field: "key1", defaultValue?: string | undefined): string watch("key1", "test"); // function watch<"key1">(field: "key1", defaultValue?: string | undefined): string watch("key1", true); // ❌: type error watch("key3.key1"); // function watch<unknown>(field: string, defaultValue?: unknown): unknown watch("key3.key1", 1); // function watch<1>(field: string, defaultValue?: 1 | undefined): number watch("key3.key1", "test"); // function watch<"key3.key1", "test">(field: "key3.key1", defaultValue?: string | undefined): string watch("key3.key2", true); // function watch<true>(field: string, defaultValue?: true | undefined): boolean watch(["key1", "key2"]); // function watch<"key1" | "key2">(fields: ("key1" | "key2")[], defaultValues?: DeepPartial<Pick<FormValues, "key1" | "key2">> | undefined): Pick<FormValues, "key1" | "key2"> watch(["key1", "key2"], { key1: "test" }); // function watch<"key1" | "key2">(fields: ("key1" | "key2")[], defaultValues?: DeepPartial<Pick<FormValues, "key1" | "key2">> | undefined): Pick<FormValues, "key1" | "key2"> watch(["key1", "key2"], { key1: "test", key2: true }); // ❌: type error watch(["key1", "key3.key1"], { key1: "string" }); // function watch(fields: string[], defaultValues?: DeepPartial<FormValues> | undefined): DeepPartial<FormValues> watch(["key1", "key3.key1"], { test: "string" }); // ❌: type error watch<string, FormData["key3"]["key1"]>("key3.key1"); // => string watch<string, FormData["key3"]["key2"]>("key3.key2"); // => string return <form />; }
handleSubmit:
((data: Object, e?: Event) => void, (errors: Object, e?: Event) => void) => Function
当表单校验成功时,此函数将传递表单数据并可以远程调用。
handleSubmit(onSubmit)()
注意: 您可以传递async
函数用于异步校验。例如:
handleSubmit(async (data) => await fetchAPI(data))
import React from "react"; import { useForm } from "react-hook-form"; export default function App() { const { register, handleSubmit } = useForm(); const onSubmit = (data, e) => console.log(data, e); const onError = (errors, e) => console.log(errors, e); return ( <form onSubmit={handleSubmit(onSubmit, onError)}> <input name="firstName" ref={register} /> <input name="lastName" ref={register} /> <button type="submit">Submit</button> </form> ); }
import React from "react"; import { useForm, SubmitHandler } from "react-hook-form"; type FormValues = { firstName: string; lastName: string; email: string; }; export default function App() { const { register, handleSubmit } = useForm<FormValues>(); const onSubmit: SubmitHandler<FormValues> = data => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="firstName" ref={register} /> <input name="lastName" ref={register} /> <input name="email" type="email" ref={register} /> <input type="submit" /> </form> ); }
reset: (values?: Record<string, any>, omitResetState?: Record<string, boolean>) => void
此函数将重置表单中的字段值和错误。通过提供omitResetState
,您就可以 部分状态。可以将values
作为可选参数传递,以将表单重置为默认值。
注意: 对于像React-Select
这样没有暴露ref
的受控组件,您必须通过手动重置或使用来包装您的受控组件。
注意: 您将需要在useForm
传入defaultValues
来重置Controller
组件的值。
import React from "react"; import { useForm } from "react-hook-form"; export default function App() { const { register, handleSubmit, reset } = useForm(); const onSubmit = (data, e) => {}; useEffect(async () => { const result = await fetch('./api/formValues.json'); // result: { firstName: 'test', lastName: 'test2' } reset(result); // asynchronously reset your form values }, [reset]) return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="firstName" ref={register({ required: true })} /> <input name="lastName" ref={register} /> <input type="reset" /> // standard reset button <input type="button" onClick={() => reset({ firstName: "bill" }); }} /> // reset form with values <input type="button" onClick={() => { reset({ firstName: "bill" }, { errors: true, // errors will not be reset dirtyFields: true, // dirtyFields will not be reset isDirty: true, // dirty will not be reset isSubmitted: false, touched: false, isValid: false, submitCount: false, }); }} /> </form> ); }
import React from "react"; import { useForm } from "react-hook-form"; interface UseFormInputs { firstName: string lastName: string } export default function Form() { const { register, handleSubmit, reset, errors } = useForm<UseFormInputs>(); const onSubmit = (data: UseFormInputs) => { console.log(data) }; return ( <form onSubmit={handleSubmit(onSubmit)}> <label>First name</label> <input name="firstName" ref={register({ required: true })} /> <label>Last name</label> <input name="lastName" ref={register} /> <input type="submit" /> <input type="reset" value="Standard Reset Field Values" /> <input type="button" onClick={() => reset()} value="Custom Reset Field Values & Errors" /> </form> ); }
setError:
(name: string, error: { type?: string, types?: object, message?: string, shouldFocus?: boolean }) => void
该函数允许您手动设置输入单个或多项错误。
如果输入组件的值通过校验,此方法将不会保留相关的输入错误。
设置与输入组件字段无关的错误,并将在通过
clearErrors
手动删除之前一直保留。可用于
handleSubmit
函数调用后,向用户提供异步校验的错误反馈。(API返回了校验错误)
import React from "react"; import { useForm } from "react-hook-form"; const App = () => { const { register, handleSubmit, setError, errors } = useForm(); const onSubmit = data => { console.log(data) }; return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="username" onChange={() => { setError("username", { type: "manual", message: "Dont Forget Your Username Should Be Cool!" }); }} ref={register} /> {errors.username && <p>{errors.username.message}</p>} <input type="submit" /> </form> ); };
import * as React from "react"; import { useForm } from "react-hook-form"; type FormInputs = { username: string; }; const App = () => { const { register, handleSubmit, setError, errors } = useForm<FormInputs>(); const onSubmit = (data: FormInputs) => { console.log(data) }; return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="username" onChange={() => { setError("username", { type: "manual", message: "Dont Forget Your Username Should Be Cool!" }); }} ref={register} /> {errors.username && <p>{errors.username.message}</p>} <input type="submit" /> </form> ); };
clearErrors: (name?: string | string[]) => void
undefined
: 重置所有错误string
: 重置单个表单项错误string[]
: 重置指定所有表单项的错误
import * as React from "react"; import { useForm } from "react-hook-form"; const App = () => { const { register, errors, handleSubmit, clearErrors } = useForm(); const onSubmit = data => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="firstName" ref={register({ required: true })} /> <input name="lastName" ref={register({ required: true })} /> <input name="username" ref={register({ required: true })} /> <button type="button" onClick={() => clearErrors("firstName")}> Clear First Name Errors </button> <button type="button" onClick={() => clearErrors(["firstName", "lastName"])} > Clear First and Last Name Errors </button> <button type="button" onClick={() => clearErrors()}> Clear All Errors </button> <input type="submit" /> </form> ); };
import * as React from "react"; import { useForm } from "react-hook-form"; import "./styles.css"; type FormInputs = { firstName: string; lastName: string; username: string; }; const App = () => { const { register, errors, handleSubmit, clearErrors } = useForm<FormInputs>(); const onSubmit = (data: FormInputs) => { console.log(data) }; return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="firstName" ref={register({ required: true })} /> <input name="lastName" ref={register({ required: true })} /> <input name="username" ref={register({ required: true })} /> <button type="button" onClick={() => clearErrors("firstName")}> Clear First Name Errors </button> <button type="button" onClick={() => clearErrors(["firstName", "lastName"])} > Clear First and Last Name Errors </button> <button type="button" onClick={() => clearErrors()}> Clear All Errors </button> <input type="submit" /> </form> ); };
setValue: (name: string, value: any, config?: Object) => void
该函数允许您动态设置注册过的表单项的值。 同时,它也会尝试避免不必要的重渲染。与只有以下情况将触发重渲染:
值更新时未通过错误校验
值更新时通过错误校验
首次调用且
formState.isDirty
为true
可以将shouldValidate
参数设置为true
以强制触发表单项的校验。
setValue('name', 'value', { shouldValidate: true })
您也可以将shouldDirty
参数设置为true
,以将表单项设置为dirty。
setValue('name', 'value', { shouldDirty: true })
import * as React from "react"; import { useForm } from "react-hook-form"; const App = () => { const { register, handleSubmit, setValue } = useForm(); const onSubmit = data => { console.log(data) }; return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="firstName" ref={register} /> <input name="lastName" ref={register} /> <button onClick={() => setValue("firstName", "Bill")}> Set First Name Value </button> <button onClick={() => setValue("lastName", "Luo", { shouldValidate: true, shouldDirty: true }) } > Set Last Name </button> <input type="submit" /> </form> ); };
import * as React from "react"; import { useForm } from "react-hook-form"; type FormInputs = { firstName: string lastName: string } const App = () => { const { register, handleSubmit, setValue } = useForm<FormInputs>(); const onSubmit = (data: FormInputs) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="firstName" ref={register} /> <input name="lastName" ref={register} /> <button onClick={() => setValue("firstName", "Bill")}> Set First Name Value </button> <button onClick={() => setValue("lastName", "Luo", { shouldValidate: true, shouldDirty: true }) } > Set Last Name </button> <input type="submit" /> </form> ); };
import React from "react"; import { useForm } from "react-hook-form"; type FormValues = { string: string; number: number; object: { number: number; boolean: boolean; }; array: { string: string; boolean: boolean; }[]; }; export default function App() { const { setValue } = useForm<FormValues>(); setValue("string", "test"); // function setValue<"string", string>(name: "string", value: string, shouldValidate?: boolean | undefined): void setValue("number", 1); // function setValue<"number", number>(name: "number", value: number, shouldValidate?: boolean | undefined): void setValue("number", "error"); return <form />; }
getValues: (payload?: string | string[]) => Object
优化过的读取表单值的方法。不会像watch
一样触发重渲染及监听输入组件变化。
getValues()
:读取整个表单值。getValues('test')
:通过name读取单个输入组件值。getValues(['test','test1'])
:通过name读取多个输入组件值。
import React from "react"; import { useForm } from "react-hook-form"; export default function App() { const { register, getValues } = useForm(); return ( <form> <input name="test" ref={register} /> <input name="test1" ref={register} /> <button type="button" onClick={() => { const values = getValues(); // { test: "test-input", test1: "test1-input" } const singleValue = getValues("test"); // "test-input" const multipleValues = getValues(["test", "test1"]); // { test: "test-input", test1: "test1-input" } }} > Get Values </button> </form> ); }
import React from "react"; import { useForm } from "react-hook-form"; type FormInputs = { test: string test1: string } export default function App() { const { register, getValues } = useForm<FormInputs>(); return ( <form> <input name="test" ref={register} /> <input name="test1" ref={register} /> <button type="button" onClick={() => { const values = getValues(); // { test: "test-input", test1: "test1-input" } const singleValue = getValues("test"); // "test-input" const multipleValues = getValues(["test", "test1"]); // { test: "test-input", test1: "test1-input" } }} > Get Values </button> </form> ); }
import React from "react"; import { useForm } from "react-hook-form"; // Flat input values type Inputs = { key1: string; key2: number; key3: boolean; key4: Date; }; export default function App() { const { register, getValues } = useForm<Inputs>(); getValues(); return <form />; } // Nested input values type Inputs1 = { key1: string; key2: number; key3: { key1: number; key2: boolean; }; key4: string[]; }; export default function Form() { const { register, getValues } = useForm<Inputs1>(); getValues(); // function getValues(): Record<string, unknown> getValues("key1"); // function getValues<"key1", unknown>(payload: "key1"): string getValues("key2"); // function getValues<"key2", unknown>(payload: "key2"): number getValues("key3.key1"); // function getValues<"key3.key1", unknown>(payload: "key3.key1"): unknown getValues<string, number>("key3.key1"); // function getValues<string, number>(payload: string): number getValues<string, boolean>("key3.key2"); // function getValues<string, boolean>(payload: string): boolean getValues("key4"); // function getValues<"key4", unknown>(payload: "key4"): string[] return <form />; }
trigger: (payload?: string | string[]) => Promise<boolean>
手动触发表单校验。
trigger()
: 触发所有表单项的校验。trigger('test')
: 触发name对应的单个表单项校验。trigger(['test', 'test1'])
: 触发name对应的多个表单项校验。
import React from "react"; import { useForm } from "react-hook-form"; export default function App() { const { register, trigger, errors } = useForm(); return ( <form> <input name="firstName" ref={register({ required: true })} /> <input name="lastName" ref={register({ required: true })} /> <button type="button" onClick={() => { trigger("lastName"); }}>Trigger</button> <button type="button" onClick={() => { trigger(["firstName", "lastName"]); }}>Trigger Multiple</button> <button type="button" onClick={() => { trigger(); }}>Trigger All</button> <button type="button" onClick={async () => { const result = await trigger("lastName"); if (result) { console.log("valid input") } }} > Trigger Result </button> </form> ); }
import React from "react"; import { useForm } from "react-hook-form"; type FormInputs = { firstName: string lastName: string } export default function App() { const { register, trigger, errors } = useForm<FormInputs>(); return ( <form> <input name="firstName" ref={register({ required: true })} /> <input name="lastName" ref={register({ required: true })} /> <button type="button" onClick={() => { trigger("lastName"); }}>Trigger</button> <button type="button" onClick={() => { trigger(["firstName", "lastName"]); }}>Trigger Multiple</button> <button type="button" onClick={() => { trigger(); }}>Trigger All</button> <button type="button" onClick={async () => { const result = await trigger("lastName"); if (result) { console.log("valid input") } }} > Trigger Result </button> </form> ); }
control: Object
为React Hook Form的Controller组件提供的对象,包含了 React Hook Form 注册受控组件所需的方法。
import React from "react"; import { useForm, Controller } from "react-hook-form"; function App() { const { control } = useForm(); return ( <Controller as={<input />} name="firstName" control={control} defaultValue="" /> ); }
import React from "react"; import { useForm, Controller } from "react-hook-form"; import { TextField } from "@material-ui/core"; type FormInputs = { firstName: string } function App() { const { control, handleSubmit } = useForm<FormInputs>(); const onSubmit = (data: FormInputs) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <Controller as={TextField} name="firstName" control={control} defaultValue="" /> <input type="submit" /> </form> ); }
formState: Object
包含表单状态信息的对象。
重要:formState
使用Proxy包装以提高渲染性能,因此请使您在渲染前先调用/读取它,以使状态可以更新。 减少重渲染的功能仅适用于Web平台,React Native因不支持Proxy
而无法使用。
名字 | 类型 | 説明 |
---|---|---|
isDirty | boolean | 在用户与任何输入组件交互后设置为true 。注意:由于取消文件选择功能以及 FileList 对象,文件型输入组件需要在应用层管理。 |
dirtyFields | object | 用户更改过的表单项。 |
touched | object | 包含用户交互过的所有表单项的对象。 |
isSubmitted | boolean | 在用户提交表单后设置true 。提交表单后,其状态将保持提交状态,直到使用reset方法调用为止。 |
isSubmitSuccessful | boolean | 表示表格已成功提交。 |
isSubmitting | boolean | 在表单提交将设置为true ,提交后设置为false 。 |
submitCount | number | 提交表单次数。 |
isValid | boolean | 如果没有任何错误,设置为 true 。Note: |
isValidating | boolean | 在验证期间设置为true 。 |
errors | object | An object with field errors. |
import React from "react"; import { useForm } from "react-hook-form"; export default function App() { const { register, handleSubmit, errors, // Read the formState before render to subscribe the form state through Proxy formState: { isDirty, isSubmitting, touched, submitCount }, } = useForm(); const onSubmit = (data: FormInputs) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="test" ref={register} /> <input type="submit" /> </form> ); }
import React from "react"; import { useForm } from "react-hook-form"; type FormInputs = { test: string } export default function App() { const { register, handleSubmit, errors, // Read the formState before render to subscribe the form state through the Proxy formState: { isDirty, isSubmitting, touched, submitCount }, } = useForm<FormInputs>(); const onSubmit = (data: FormInputs) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input name="test" ref={register} /> <input type="submit" /> </form> ); }
Controller: Component
React Hook Form优先采用非受控组件和原生的输入组件, 然而难以避免使用到第三方的受控组件,如React-Select, AntD 和 Material-UI。 这个包装组件将使您的工作更轻松。
名字 | 类型 | 需要 | 説明 |
---|---|---|---|
name | string | ✓ | 输入组件的唯一名称。 |
as | React.ElementType |
你传给 注意: 以下属性将会传入你的组件
| |
render | Function | 这是一个render prop。返回React元素并将事件和值附加到组件中的函数: 。这很容易与带有非标准属性名称的外部受控组件集成,为子组件提供onChange ,onBlur 和value 属性。 。
| |
control | Object | ✓ | 通过调用useForm 得到的对象。使用FormContext时可选。 |
defaultValue | any | 与非受控组件的defaultValue 相同,当传入boolean 值时,它将被视为复选框输入。注意: 需要在表单项层级使用 注意: 如果您的表单需要在 | |
rules | Object | 与register 格式一致的校验规则
| |
onFocus | () => void | 此回调允许自定义hook在有错误情况聚焦到对应的输入组件。无论React和React-Native 只要组件可以聚焦,它都可以使用。 CodeSandbox |
import React from "react"; import ReactDatePicker from "react-datepicker"; import { TextField } from "@material-ui/core"; import { useForm, Controller } from "react-hook-form"; function App() { const { handleSubmit, control } = useForm(); return ( <form onSubmit={handleSubmit(data => console.log(data))}> <Controller as={TextField} name="TextField" control={control} defaultValue="" /> <Controller control={control} name="ReactDatepicker" render={({ onChange, onBlur, value }) => ( <ReactDatePicker onChange={onChange} onBlur={onBlur} selected={value} /> )} /> <input type="submit" /> </form> ); }
import React from "react"; import ReactDatePicker from "react-datepicker"; import { TextField } from "@material-ui/core"; import { useForm, Controller } from "react-hook-form"; type FormValues = { TextField: string; ReactDatepicker: string; } function App() { const { handleSubmit, control } = useForm<FormValues>(); return ( <form onSubmit={handleSubmit(data => console.log(data))}> <Controller as={TextField} name="TextField" control={control} defaultValue="" /> <Controller control={control} name="ReactDatepicker" render={({ onChange, onBlur, value }) => ( <ReactDatePicker onChange={onChange} onBlur={onBlur} selected={value} /> )} /> <input type="submit" /> </form> ); }
useController: (arguments: UseControllerProps) => { field: object, meta: object }
这个自定义的钩子是Controller
的动力,并且分享了 和Controller
一样的道具和方法。这对于 创建可重用的Controller输入,而Controller
是 灵活的选项,可以放到您的页面或表单中。
请参考Controller
部分,了解这个钩子的功能。 参数。除了as
和 之外,它与其他参数相同。渲染
。
Object Name | 名字 | 类型 | 説明 |
---|---|---|---|
field | onChange | (value: any) => void | A function which send value to hook form and should be assigned with |
onBlur | (value: any) => void | A function which send value to hook form and should be assigned with | |
value | unknown | The value of this controlled component. | |
ref | |||
Assign | |||
meta | invalid | boolean | Invalid state for current input. |
isTouched | boolean | Touched state for current controlled input. | |
isDirty | boolean | Dirty state for current controlled input. |
import React from "react"; import { TextField } from "@material-ui/core"; import { useController, control } from "react-hook-form"; function Input({ control, name }) { const { field: { ref, ...inputProps }, meta: { invalid, isTouched, isDirty }, } = useController({ name, control, rules: { required: true }, defaultValue: "", }); return <TextField {...inputProps} inputRef={ref} />; } function App() { const { control } = useForm(); return <Input name="firstName" control={control} />; }
import * as React from "react"; import { useForm, useController } from "./src"; function Input(props) { const { field, meta } = useController(props); return ( <div> <input {...field} placeholder={props.name} /> <p>{meta.isTouched && "Touched"}</p> <p>{meta.isDirty && "Dirty"}</p> <p>{meta.invalid ? "invalid" : "valid"}</p> </div> ); } export default function App() { const { handleSubmit, control } = useForm({ defaultValues: { FirstName: "" }, mode: "onChange" }); const onSubmit = (data) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <Input control={control} name="FirstName" rules={{ required: true }} /> <input type="submit" /> </form> ); }
ErrorMessage: Component
展示输入组件错误信息的简易组件
npm install @hookform/error-message
名字 | 类型 | 需要 | 説明 |
---|---|---|---|
name | string | ✓ | 表单项名称 |
errors | object | React Hook Form中的errors 对象。如果您使用的是FormProvider,则为可选。 | |
message | string | React.ReactElement | 内联错误信息。 | |
as | React.ElementType | string | 包装组件或HTML标签。 例如: as="span" or as={<Text />} | |
render | ({ message: string | React.ReactElement, messages?: Object}) => any | 这是一个 render prop 用于呈现单个或多个错误信息。 注意:您需要设置将 |
import React from "react"; import { useForm } from "react-hook-form"; import { ErrorMessage } from '@hookform/error-message'; export default function App() { const { register, formState: { errors }, handleSubmit } = useForm(); const onSubmit = data => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("singleErrorInput", { required: "This is required." })} /> <ErrorMessage errors={errors} name="singleErrorInput" /> <ErrorMessage errors={errors} name="singleErrorInput" render={({ message }) => <p>{message}</p>} /> <input type="submit" /> </form> ); }
import React from "react"; import { useForm } from "react-hook-form"; import { ErrorMessage } from '@hookform/error-message'; interface FormInputs { singleErrorInput: string } export default function App() { const { register, formState: { errors }, handleSubmit } = useForm<FormInputs>(); const onSubmit = (data: FormInputs) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("singleErrorInput", { required: "This is required." })} /> <ErrorMessage errors={errors} name="singleErrorInput" /> <ErrorMessage errors={errors} name="singleErrorInput" render={({ message }) => <p>{message}</p>} /> <input type="submit" /> </form> ); }
useFormContext: Function
可以获取表单context
的 hook函数 useFormContext
用于那些难以传递context作为属性的嵌套结构很深的场景。
使用useFormContext
需要用FormProvider
包装表单。
Props
名字 | 类型 | 説明 |
---|---|---|
...props | Object | Accept all useForm methods. |
import React from "react"; import { useForm, FormProvider, useFormContext } from "react-hook-form"; export default function App() { const methods = useForm(); const onSubmit = data => console.log(data); return ( <FormProvider {...methods} > // pass all methods into the context <form onSubmit={methods.handleSubmit(onSubmit)}> <NestedInput /> <input type="submit" /> </form> </FormProvider> ); } function NestedInput() { const { register } = useFormContext(); // retrieve all hook methods return <input {...register("test")} />; }
useWatch: ({ control?: Control, name?: string, defaultValue?: any }) => object
与watch
API功能相近,但是, 会在您的组件级别区隔重渲染,并可能为您的应用程序提供更好的性能。
Props
名字 | 类型 | 説明 |
---|---|---|
name | string | string[] | 表单项名称 |
control | Object | 通过调用useForm 得到的对象。使用FormContext时可选。 |
defaultValue | any | default value for Note: the first render will always return |
disable | boolean = false | Option to disable the subscription. |
Rules
The initial return value from
useWatch
will always return what's inside ofdefaultValue
ordefaultValues
fromuseForm
.The only different between
useWatch
andwatch
is at root level or component level update.useWatch
execution order matters, which means if you are update form value before the subscription is in place, then the value update will be ignored.setValue('test', 'data'); useWatch('test'); // ❌ subscription is happened after value update, no update received useWatch('example'); // ✅ input value update will be received and trigger re-render setValue('example', 'data');
useWatch
result is optimised for render phase instead ofuseEffect
's deps, to detect value update you may want to use an external custom hook for value comparison.
import React from "react"; import { useForm, useWatch } from "react-hook-form"; function IsolateReRender({ control }) { const firstName = useWatch({ control, name: 'firstName', // without supply name will watch the entire form, or ['firstName', 'lastName'] to watch both defaultValue: 'default' // default value before the render }); return <div>{firstName}</div>; // only re-render at the component level, when firstName changes } function App() { const { register, control, handleSubmit } = useForm(); return ( <form onSubmit={handleSubmit(data => console.log("data", data))}> <input {...register("firstName")} /> <input {...register("last")} /> <IsolateReRender control={control} /> <input type="submit" /> </form> ); }
import React from "react"; import { useForm, useWatch } from "react-hook-form"; interface FormInputs { firstName: string } function FirstNameWatched({ control }: { control: Control<FormInputs> }) { const firstName = useWatch({ control, name: "firstName", // without supply name will watch the entire form, or ['firstName', 'lastName'] to watch both defaultValue: "default" // default value before the render }); return <p>Watch: {firstName}</p>; // only re-render at the component level, when firstName changes } function App() { const { register, control, handleSubmit } = useForm<FormInputs>(); const onSubmit = (data: FormInputs) => { console.log(data) }; return ( <form onSubmit={handleSubmit(onSubmit)}> <label>First Name:</label> <input {...register("firstName")} /> <input type="submit" /> <FirstNameWatched control={control} /> </form> ); }
useFieldArray:
({ control?: Control, name: string, keyName?: string = 'id' }) => objectVideo
处理非受控表单项列表(一组动态的输入组件)的自定义 hook。 用于提高用户体验和性能。可以观看 这个短视频 以比较受控/非受控的表单项列表。
名字 | 类型 | 需要 | 説明 |
---|---|---|---|
name | string | 表单项名称。重要: 确保名称为对象键结构: name=test[index].name 因为我们不支持扁平数组。 | |
control | Object | 通过调用useForm 得到的对象。使用FormContext时可选。 | |
keyName | string = 'id' | 表单项列表key 的值,默认为“ id”,您可以 更改key的名称。 |
function Test() { const { control, register } = useForm(); const { fields, append, prepend, remove, swap, move, insert } = useFieldArray({ control, // control props comes from useForm (optional: if you are using FormContext) name: "test", // unique name for your Field Array // keyName: "id", default to "id", you can change the key name }); return ( {fields.map((field, index) => ( <input key={field.id} // important to include key with field's id name={`test[${index}].value`} ref={register()} // register() when there is no validation rules defaultValue={field.value} // make sure to include defaultValue /> ))} ); }
重要: useFieldArray
建立在非受控组件之上。 以下说明将帮助您了解并牢记其实施过程中的行为。
可以通过在
userform
中的defaultValues
来填充表单项列表
。确保您将来自
fields
对象的id
分配为组件键。确保通过
fields[index]
设置defaultValue
。您不能在动作里调用其他的动作,而应该在每次渲染中触发动作。
// ❌ The following is not correct handleChange={() => { if (fields.length === 2) { remove(0); } append({ test: 'test' }); }} // ✅ The following is correct and second action is triggered after next render handleChange={() => { append({ test: 'test' }); }} React.useEffect(() => { if (fields.length === 2) { remove(0); } }, [fields])
与
useFieldArray
一起使用时,请使用ref={register()}
而不是ref={register}
,这一点很重要,使得register
将在映射期间被调用。- 它不适用于
useEffect
上的自定义注册或者是条件渲染。对于条件渲染的场景,尝试使用样式控制组件是否可见,以及控制校验函数。 在
watch
整个表单项列表时,需要在fields
设置初始值以避免返回空值。例如:watch('fieldArray', fields)
当所有的输入组件都从表单项列表移除时,
watch
将返回defaultValues
。你可以使用fields.length
来避免这个行为。例如;fields.length ? watch('fieldArray', fields) : []
名字 | 类型 | 説明 |
---|---|---|
fields | object & { id: string } | 此对象是映射和渲染输入组件的数据来源 重要: 由于每个输入可以是非受控的, 因此 eg: |
append |
| 将单个/多个输入组件追加到表单项列表的末尾并聚焦。 |
prepend |
| 将单个/多个输入组件插入到表单项列表的头部并聚焦。 |
insert |
| 将单个/多个输入组件插入到表单项列表的特定位置并聚焦。 |
swap |
| 交换单个/多个输入组件的位置 |
move |
| 将单个/多个输入组件移动到另一位置。 |
remove |
| 在特定位置删除单个/多个输入组件,不传入索引时删除列表内全部表单项。 |
import React from "react"; import { useForm, useFieldArray } from "react-hook-form"; function App() { const { register, control, handleSubmit, reset, trigger, setError } = useForm({ // defaultValues: {}; you can populate the fields by this attribute }); const { fields, append, prepend, remove, swap, move, insert } = useFieldArray({ control, name: "test" }); return ( <form onSubmit={handleSubmit(data => console.log(data))}> <ul> {fields.map((item, index) => ( <li key={item.id}> <input name={`test[${index}].firstName`} ref={register()} defaultValue={item.firstName} // make sure to set up defaultValue /> <Controller as={<input />} name={`test[${index}].lastName`} control={control} defaultValue={item.lastName} // make sure to set up defaultValue /> <button type="button" onClick={() => remove(index)}>Delete</button> </li> ))} </ul> <button type="button" onClick={() => append({ firstName: "appendBill", lastName: "appendLuo" })} > append </button> <input type="submit" /> </form> ); }
进阶
了解如何使用React Hook Form构建复杂且易于访问的表单。