有使用過React的同學都知道,用React做輸入控制的時候, 通常做法是建立一個input的state, 具體如下:
import { useState } from "react";
export default function Form() {
const [surname, setSurname] = useState("");
const [firstName, setFirstName] = useState("");
const [age, setAge] = useState(0);
const onFormSumbit = (e) => {
e.preventDefault();
console.log("Form Submitted");
const formData = new FormData(e.target);
for (const pair of formData.entries()) {
console.log(`${pair[0]}: ${pair[1]}`);
}
};
//every single rendering trigger the following code once
console.log("Form rendered");
return (
<form onSubmit={onFormSumbit}>
<h1>Using Pure React</h1>
First Name:
<input
onChange={(e) => {
setFirstName(e.target.value);
}}
name="firstName"
value={firstName || ""}
/>
<br />
Surname:
<input
onChange={(e) => {
setSurname(e.target.value);
}}
name="surname"
value={surname || ""}
/>
<br />
Age:
<input
onChange={(e) => {
setAge(e.target.value);
}}
name="age"
value={age || ""}
type="number"
/>
<br />
<input type="submit" />
</form>
);
}
事實上React的官方best practise也是這樣的。 但是當製作有多個輸入項的表單時, 事情就麻煩了。 不單每個輸入項都要設定一個State給他, 代碼變得臃腫難閱讀。 更嚴重的是, 每次用戶填寫表單的時候, 即使是再微小的改動, 也會觸發整個表單的渲染(rendering)。 每增加一個字, 渲染一次,每刪除一個字,渲染一次, 那樣前端的效率會大大受到影響。
解決這類問題的辦法有很多,例如我們可以使用useEffect去優化渲染時的效率,但這裡有一個更好的工具去完完全全避免這因為input而產生的渲染,這就是今天要介紹的React-Hook-Form。
以下是一個使用React-Hook-Form重新製作的表單:
import { useForm } from "react-hook-form";
export default function HookForm() {
const { register, handleSubmit } = useForm();
const onFormSumbit = (formObj, e) => {
e.preventDefault();
console.log("Form Submitted");
const formData = new FormData(e.target);
for (const pair of formData.entries()) {
console.log(`${pair[0]}: ${pair[1]}`);
}
};
//every single rendering trigger the following code once
console.log("Form rendered");
return (
<form onSubmit={handleSubmit(onFormSumbit)}>
<h1>Using React-Hook-Form</h1>
First Name:
<input {...register("firstName")} />
<br />
Surname:
<input {...register("surname")} />
<br />
Age:
<input {...register("age")} />
<br />
<input type="submit" />
</form>
);
}
你會看到與傳統表單最大的分別是已經沒有了setState, 取而代之是一次useForm, 以及在input裏面的register函數。
再來看看它在填寫表單時的渲染次數
什麼!只有1次!?對, 這就是React-Hook-Form的神奇魔法。它為什麼做到如此強大的功能呢, 關鍵就在於一個字:ref
<input {...register("firstName")} />
這個register函數會返回一個包含4個屬性(attribute)的物件(object),那4個屬性分別是name,onChange,onBlur,以及ref。這裡的name會是我們傳入register的第一個參數,通常是輸入項的名稱。onChange,onBlur 傳入對應的event handler。最後的ref屬性提供了一個接口,讓React-Hook-Form能繞過React的Virtual Dom,訪問input真實的DOM nodes。
因為React-Hook-Form 在最後提交的時候才通過ref取得DOM nodes的數值,input的數值根本沒有綁定在React裡。所以我們改變input的value,也不會觸發渲染。
對比單純用state做的表單,React-Hook-Form提供了一個簡單易用的介面,除了讓setup變得更快,更解決了輸入渲染的問題,在建立多個或者複雜表單時會很有用。
React-Hook-Form亦提供了諸多如數據驗證等等其他表單常見的功能,十分推介大家嘗試這個Library。