您好,登錄后才能下訂單哦!
這篇“React Hook Form優雅處理表單使用的方法是什么”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“React Hook Form優雅處理表單使用的方法是什么”文章吧。
先說說受控組件,以 input 為例:
const [value,setValue] = useState('') <input value={value} onChange={(e)=> setValue(e.target.value)} />
在上面的代碼中,我們通過通過自己維護一個 state 來獲取或更新 input 輸入的值,以這種方式控制取值的 表單輸入元素 就叫做 受控組件。
那么什么是非受控組件呢?在 React 中,非受控組件是指表單元素的值由 DOM 節點直接處理,而不是由 React 組件來管理。如下例:
import React, { useRef } from 'react'; function UncontrolledForm() { const nameInputRef = useRef(null); const emailInputRef = useRef(null); const passwordInputRef = useRef(null); function handleSubmit(event) { console.log('Name:', nameInputRef.current.value); console.log('Email:', emailInputRef.current.value); console.log('Password:', passwordInputRef.current.value); event.preventDefault(); } return ( <form onSubmit={handleSubmit}> <label> Name: <input type="text" ref={nameInputRef} /> </label> <label> Email: <input type="email" ref={emailInputRef} /> </label> <label> Password: <input type="password" ref={passwordInputRef} /> </label> <button type="submit">Submit</button> </form> ); }
在這個例子中,我們使用 useRef
Hook 創建了一個 ref 對象,并將其賦值給每個 input 元素的 ref
屬性。在 handleSubmit
函數中,我們使用 ref.current.value
來獲取每個 input 元素的值。這里的每個input 元素都是非受控組件,因為它們的值由 DOM 節點直接處理,而不是由 React 組件來管理。
當然,這意味著當用戶輸入數據時,React 無法追蹤表單元素的值。因此,當您需要訪問表單元素的值時,您需要使用DOM API來獲取它們。
在 React 中,通常使用受控組件來處理表單。受控組件表單元素的值由 React 組件來管理,當表單數據發生變化時,React 會自動更新組件狀態,并重新渲染組件。這種方式可以使得表單處理更加可靠和方便,也可以使得表單數據和應用狀態之間保持一致。
但在實際的開發中,表單往往是最復雜的場景,有的表單有數十個字段,如果使用受控組件去構建表單,那么我們就需要維護大量 state,且 React 又不像 Vue 可以通過雙向綁定直接修改 state 的值,每一個表單字段還需要定義一下 onChange
方法。因此在維護復雜表單時,使用受控組件會有很大的額外代碼量。
為了解決受控組件帶來的問題,我們可以使用非受控組件來構建表單。受控組件主要有以下三個優點
可以減少組件的 代碼量和復雜度,因為非受控組件不需要在組件狀態中保存表單數據。
可以更好地 處理大量表單數據,因為非受控組件可以讓您直接操作DOM元素,而不需要將所有表單數據存儲在組件狀態中。
可以更容易地與第三方 JavaScript 庫和表單處理代碼集成,因為非受控組件使您能夠使用 DOM API 或 ref 直接訪問表單元素,而不是在 React 中重新實現所有的表單處理邏輯。
React Hook Form 是一個基于 React 的 輕量級表單驗證庫。它使用了 React Hook API,讓表單驗證變得簡單、易用、高效。React Hook Form 的主要目標是提供一個簡單易用的表單驗證解決方案,同時還能保持高性能和低開銷。
React Hook Form 的特點都在官網首頁 react-hook-form.com 中以可交互的形式展示,包括了以下幾點:
通過 React Hook 的方式減少使用時的代碼量,簡單易上手,并且移除了不必要的重復渲染:
隔離重復渲染,自組件重新渲染時不會觸發父組件或兄弟組件的重新渲染:
訂閱機制,與上一點相似,能夠訂閱單個輸入和表單狀態更新,而無需重新呈現整個表單:
組件渲染速度足夠快,而且代碼庫非常小,壓縮后只有幾 KB 大小,不會對頁面性能造成任何影響:
先看看最基礎的表單實現:
import React from "react"; import { useForm } from "react-hook-form"; function MyForm() { const { register, handleSubmit } = useForm(); const onSubmit = (data) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("firstName")} /> <input {...register("lastName")} /> <button type="submit">Submit</button> </form> ); }
咱們來分析一下這段代碼:
使用 useForm
函數創建一個表單對象,該函數返回一個包含 register
和 handleSubmit
等方法的對象。
在表單中定義兩個輸入框,使用 register
函數注冊表單輸入組件,并指定組件的名稱為 firstName
和 lastName
。
使用 React Hook Form 提供的 handleSubmit
函數來管理表單的提交和數據驗證。
這里我們不需要定義任何 state 即可在 submit 時獲取到表單中的數據,接下來我們補充一下基本的表單驗證和錯誤提示:
import React from "react"; import { useForm } from "react-hook-form"; function MyForm() { const onSubmit = (data) => { console.log(data); }; const { register, handleSubmit, formState: { errors } } = useForm(); return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("firstName", { required: true })} /> {errors.firstName && <p>First name is required.</p>} <input {...register("lastName", { required: true })} /> {errors.lastName && <p>Last name is required.</p>} <button type="submit">Submit</button> </form> ); }
咱們再分析一下這段代碼:
register
函數中可以指定表單輸入組件的 驗證規則 ,例如使用 required
規則來驗證輸入框的必填項。
在表單提交處理函數中,可以調用 React Hook Form 提供的 handleSubmit
函數來自動執行表單驗證,并返回驗證結果。只有表單驗證通過才會執行 onSubmit
方法。
如果表單驗證失敗,可以使用 errors
對象來獲取每個表單輸入組件的驗證錯誤信息,并在 UI 上顯示錯誤提示。
register
函數是用來注冊表單輸入組件的,當組件注冊之后,React Hook Form 會自動收集該組件的值,并根據驗證規則進行驗證。 register
函數會返回一個對象,其中包含了一些屬性和方法,例如:
const { ref, onChange, onBlur, name } = register("firstName");
ref
屬性是一個引用,指向該輸入組件的 DOM 元素,onChange
和 onBlur
是回調函數,用于處理該組件的值變化和失去焦點事件,name
是該組件的名稱。
register
函數內部會創建一個管理表單輸入組件的對象,包含了該組件的名稱、引用、驗證規則等信息。同時,還會將該對象保存在 React Hook Form 內部的一個數據結構中。
在表單提交時,React Hook Form 會遍歷管理的所有表單輸入組件,并收集它們的值,并根據其注冊時定義的驗證規則進行驗證。
到這里,一個最基本的表單驗證及提交就已經實現了。當然,在實際開發中,表單之所以復雜是由于各種條件渲染及表單嵌套引起的,那么我們接下來再看看使用 React Hook Form 如何處理這些場景。
還是一樣,先看看示例:
父級表單:
// ParentForm.jsx import React from "react"; import { useForm, FormProvider } from "react-hook-form"; import ChildForm from "./ChildForm"; function ParentForm() { const methods = useForm(); const { register, handleSubmit, formState: { errors } } = methods; const onSubmit = (data) => { console.log(data); }; return ( <FormProvider {...methods}> <form onSubmit={handleSubmit(onSubmit)}> <h3>Parent Form</h3> <label htmlFor="firstName">First Name</label> <input {...register("firstName", { required: true })} /> {errors.firstName && <p>First name is required.</p>} <label htmlFor="lastName">Last Name</label> <input {...register("lastName", { required: true })} /> {errors.lastName && <p>Last name is required.</p>} <ChildForm/> <button type="submit">Submit</button> </form> </FormProvider> ); } export default ParentForm;
子級表單:
import React from "react"; import { useFormContext } from "react-hook-form"; function ChildForm() { const { register, errors } = useFormContext(); return ( <div> <h3>Child Form</h3> <label htmlFor="childFirstName">Child First Name</label> <input {...register("child.firstName", { required: true })} /> {errors.child?.firstName && <p>Child first name is required.</p>} <label htmlFor="childLastName">Child Last Name</label> <input {...register("child.lastName", { required: true })} /> {errors.child?.lastName && <p>Child last name is required.</p>} </div> ); } export default ChildForm;
分析一下這兩個組件的代碼:
在 ParentForm
組件中,我們使用 useForm
hook 來獲取表單的注冊函數、表單狀態等信息,并使用 FormProvider
組件將其傳遞給所有的子組件。
在 ChildForm
組件中,我們使用了 useFormContext
hook 來獲取父表單的注冊函數和表單狀態。
這里的兩個表單組件間并不需要咱們去單獨定義 props ,只需要將 useFormContext
與 FormProvider
搭配使用,就可以將一個嵌套表單的邏輯分離成多個組件進行處理,且可以在父級組件提交時統一獲取并處理數據。
FormProvider
是 React Hook Form 提供的一個組件,用于在 React 組件樹中向下傳遞 useForm
hook 的實例。它創建了一個 React Context,并將 useForm
hook 的實例作為 Context 的值,然后通過 Context.Provider
組件將這個值傳遞給所有子組件.
而 useFormContext
則可以在子組件中獲取到 FormProvider
提供的 useForm
hook 的返回值。在使用 useFormContext
時,不需要手動使用 Context.Provider
將值傳遞給子組件,而是可以直接從 useFormContext
中獲取,簡化嵌套表單的代碼邏輯。
import React from "react"; import { useForm } from "react-hook-form"; function ExampleForm() { const { register, handleSubmit, watch } = useForm(); const onSubmit = (data) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <label htmlFor="hasAge">Do you have an age?</label> <select {...register("hasAge")}> <option value="yes">Yes</option> <option value="no">No</option> </select> {watch("hasAge") === "yes" && ( <> <label htmlFor="age">Age</label> <input {...register("age", { required: true, min: 18 })} /> {watch("age") && <p>You must be at least 18 years old.</p>} </> )} <button type="submit">Submit</button> </form> ); } export default ExampleForm;
我們在 hasAge
輸入框上使用了一個簡單的條件渲染:只有當用戶選擇了 "Yes" 時,才會渲染 age
輸入框。然后使用 watch
函數來監聽輸入框的值,并在輸入的值小于 18 時顯示相應的錯誤信息。
watch
函數用來監聽指定的輸入并返回它們的值。在渲染輸入值和進行條件渲染時經常用到。
import React from "react"; import { useForm, useFieldArray } from "react-hook-form"; function ListForm() { const { register, control, handleSubmit } = useForm({ defaultValues: { list: [{ name: "" }, { name: "" }, { name: "" }] } }); const { fields, append, remove } = useFieldArray({ control, name: "list" }); const onSubmit = (data) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> {fields.map((field, index) => ( <div key={field.id}> <input {...register(`list.${index}.name`, { required: "This field is required" })} defaultValue={field.name} /> <button type="button" onClick={() => remove(index)}> Remove </button> </div> ))} <button type="button" onClick={() => append({ name: "" })}> Add Item </button> <button type="submit">Submit</button> </form> ); } export default ListForm;
分析一下上邊這段代碼:
在這個示例中,我們使用了 useForm
和 useFieldArray
hook 來處理一個表單列表。其中 list
屬性是一個包含 3 個空對象的數組。
使用 fields.map
方法遍歷 fields
數組,渲染出每一個列表項。
使用 remove
方法為每個列表項添加了一個 "Remove" 按鈕,使得用戶可以刪除不需要的列表項。我們還使用 append
方法添加了一個 "Add Item" 按鈕,可以添加新的列表項。
這段代碼的核心就是 useFieldArray
,它專門用于處理表單列表的場景,使用時我們將 useForm 返回的 control 傳入 useFieldArray hook 中,并為這個列表定義一個名字,hook 會為我們返回一些操作列表的方法,在遍歷渲染列表時,我們將每一個子項單獨進行注冊就可以實現表單列表的動態數據更改了。
需要注意的是,當使用 useFieldArray 處理表單中的數組字段時,每個字段都必須有一個 唯一的 key 值,這樣才能正確地進行數組的添加、刪除、更新等操作。如果數組中的字段沒有 key 值,useFieldArray
會自動為每個字段生成一個隨機的 key 值。
在內部實現上,useFieldArray
使用了 useFormContext 將 FormProvider 提供的 register
、unregister
和 setValue
函數傳遞給了 useFieldArray
,然后在 useFieldArray 內部維護一個數組 state,保存當前的數組值和對數組的操作。
當需要與第三方UI組件(如<DatePicker />
、<Select />
、<Slider />
等)集成時,如果使用register
注冊這些第三方UI組件,可能會遇到如無法正確更新表單數據、錯誤處理、性能差等問題。
因此,使用Controller
是一種更好的解決方案,可以將表單數據與 React Hook Form
狀態管理集成在一起,并使用render
函數來直接渲染第三方UI組件。下面放個例子:
import React from "react"; import { useForm, Controller } from "react-hook-form"; import { TextField, Button } from "@material-ui/core"; function ControllerForm() { const { control, handleSubmit } = useForm(); const onSubmit = (data) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <Controller name="firstName" control={control} defaultValue="" rules={{ required: true }} render={({ field }) => ( <TextField label="First Name" {...field} /> )} /> <Controller name="lastName" control={control} defaultValue="" rules={{ required: true }} render={({ field }) => ( <TextField label="Last Name" {...field} /> )} /> <Button type="submit" variant="contained" color="primary"> Submit </Button> </form> ); } export default ControllerForm;
control
是一個對象,它提供了一些方法和屬性,通過使用 control
,我們可以將 React Hook Form 中的數據與實際渲染的表單組件進行綁定,從而讓 React Hook Form 管理表單中所有的輸入和校驗邏輯。
field
是 <Controller>
組件通過 render
回調函數傳遞給其子組件的一個對象,field
對象中包含了一些屬性,如 value
、onChange
、onBlur
等,這些屬性傳遞給子組件,用于設置和更新表單控件的值,以及處理表單控件的事件,如用戶輸入、聚焦、失焦等。
Controller
的好處是可以將表單數據和表單狀態統一管理,同時避免了對表單數據的手動處理。此外,它還可以優化表單的渲染性能,并提供更好的錯誤處理機制,因為它可以自動處理錯誤消息和驗證規則。
React Hook Form 提供了完整的 TypeScript 支持:
import React from "react"; import { useForm, SubmitHandler } from "react-hook-form"; type FormValues = { firstName: string; lastName: string; age: number; }; function MyForm() { const { register, handleSubmit, formState: { errors }, } = useForm<FormValues>(); const onSubmit: SubmitHandler<FormValues> = (data) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register("firstName", { required: true })} /> {errors.firstName && <span>This field is required</span>} <input {...register("lastName", { required: true })} /> {errors.lastName && <span>This field is required</span>} <input {...register("age", { required: true, min: 18 })} /> {errors.age && ( <span> {errors.age.type === "required" ? "This field is required" : "You must be at least 18 years old"} </span> )} <button type="submit">Submit</button> </form> ); }
我們使用 FormValues
類型定義表單數據類型,并在 useForm
鉤子中使用 FormValues
泛型接口。這使得我們可以在注冊表單控件時提供正確的類型定義,并在 handleSubmit
函數中提供正確的表單數據類型。還可以使用泛型來定義錯誤消息的類型,可以用于準確地描述表單控件的錯誤狀態,并提供適當的錯誤消息。提高代碼的可讀性和可維護性。
以上就是關于“React Hook Form優雅處理表單使用的方法是什么”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。