有使用过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。