表單狀態管理曾經一直是讓前端頭疼的問題,錯誤提示,校驗規則,動態表單,重置。。。搞得人頭大。好在近幾年也出現了不少好的社區方案,比如 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
傳入子組件,可以發現,子組件更新時父組件也更新了。
useFieldArray來新增表單項
我們經常會遇到這樣的場景--新增表單項,比如新增收貨地址。use-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