表单状态管理曾经一直是让前端头疼的问题,错误提示,校验规则,动态表单,重置。。。搞得人头大。好在近几年也出现了不少好的社区方案,比如 Formik, react-hook-form, react-final-form等等,今天我们来谈谈其中的 react-hook-form。
useForm
useForm 是最基础的表单状态管理钩子,它接受以下参数:
const {
handleSubmit,
watch
} = useForm({
defaultValues: {},
mode: 'onSubmit' // onChange | onBlur | onSubmit | onTouched | all
})
mode 可以控制触发校验的时机,如果我们希望用户能尽快感知到填写出错了,可以使用 'all';
defaultValues, 如果表单从后台拉下来数据,有初始值,可以从这里传进去。
rules
rules可以用来校验值,支持以下字段:
{
required: true,
maxLength; // 最大长度
minLength; //最小长度
max: 5 // 最大值
min: 5 // 最小值
pattern: /1\d{12}/
validate: (v) => v > 100
validate: {
greaterThan: (v) => v > 100,
lessThan: (v) => v< 200
}
}
使用<Controller />
来和UI库集成
使用useForm
返回的register函数,可以很方便地使用原生html元素构建一个表单,但是大部分情况下,我们是使用UI库来开发表单的。
<Controller/>
组件接受control, name,rules和 render函数等作为属性,render函数接受field, fieldState, formState3个参数:field里面包括用来控制字段的onChange函数和value,fieldState 包含字段的校验信息。通过这些信息,我们就可以控制这个字段是应该怎么渲染;
field: { onChange, onBlur, value, name, ref },
fieldState: { invalid, isTouched, isDirty, error },
formState,
<Controller
control={control}
rules={{ required: true }}
name="test"
render={({
field: { onChange, onBlur, value, name, ref },
fieldState: { invalid, isTouched, isDirty, error },
formState,
}) => (
<Checkbox
onBlur={onBlur} // notify when input is touched
onChange={onChange} // send value to hook form
checked={value}
inputRef={ref}
/>
)}
/>
watch和useWatch 构建动态表单
经常有这样的场景,就是一个输入,会影响接下来的表单展示。由于useForm的表单状态发生变化,虽然会被记录下来,但是并不一定会触发重新渲染。当我们需要当值发生变化的时候更新UI,就要用到useWatch
或者watch
;
假设有这么一个场景,我们需要实时回显用户的输入,那么可以这么写:
const watchedName = watch('name', '');
return <div>
<label>Name</label>
<input
type="text"
{...register("name", { required: true, maxLength: 50 })}
/>
<div>name: {watchedName}</div>
</div>
这时,当输入框的字段更新时,就会触发重新渲染,从而回显用户输入的值。那么watch
和useWatch
的区别是啥呢,watch是 useForm
钩子的返回值,useWatch
是一个全新的钩子函数,在一些不需要父组件更新的场景下,可以获得更好的性能。下面这个例子将watch
传入子组件,可以发现,子组件更新时父组件也更新了。
trigger来手动触发校验
一般来说,触发发校验是当用户输入完成,失焦或者表单提交时触发的。但是有一种场景是,当后面的字段的校验依赖于前面字段的输入时,这时就要使用到trigger
了。举个例子,我们选择省之后,要触发对省下面的市的校验:
const sheng = watch("sheng");
useEffect(() => {
if (sheng) {
trigger("shi");
}
}, [sheng, trigger]);
通过trigger
我们就可以在省选项发生变化时,触发对省会字段的校验,从而及时发现问题。
useFieldArray来新增表单项
我们经常会遇到这样的场景--新增表单项,比如新增收货地址。react-hook-form为我们提供了useFieldArray
这个hook来完成这些工作:
const { register, control, handleSubmit, reset, watch } = useForm({
defaultValues: {
test: [{ firstName: "Bill", lastName: "Luo" }]
}
});
const {
fields,
append,
prepend,
remove,
swap,
move,
insert,
replace
} = useFieldArray({
control,
name: "test"
});
我们可以使用prepend
在队首插入一个表单项,append
在队尾插入一个表单项,remove
来去掉一个表单项。当然如果我们希望实时显示表单项里的数据的时候,还是要使用watch
和useWatch