表單狀態管理曾經一直是讓前端頭疼的問題,錯誤提示,校驗規則,動態表單,重置。。。搞得人頭大。好在近幾年也出現了不少好的社區方案,比如 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