#Was ist React Hook Form
Die meisten Anwendungen verfügen über Formulare zur Datenerfassung. Je nach Anwendungsfall kann die Implementierung von Formularen ziemlich komplex werden, insbesondere beim Erstellen von Enterprise-Anwendungen, die eine intuitive und einwandfreie Nutzererfahrung bieten sollen. Als Entwickler oder Entwicklerin bist du in den meisten deiner Projekte höchstwahrscheinlich schon einmal auf das Thema Formularerstellung gestoßen. Einfache Formulare lassen sich natürlich auch selbst verwalten, aber ab einem bestimmten Punkt ist es sinnvoll, eine Bibliothek zu verwenden, die einen Großteil der Arbeit beim Umgang mit komplexen Formularen abstrahiert.
React Hook Form ist eine solche Bibliothek, die bei der Verwaltung komplexer Formulare hilft. Sie bietet eine hervorragende Performance, ist extrem leichtgewichtig, kommt ohne Abhängigkeiten aus, lässt sich problemlos in verschiedene React-UI-Frameworks wie Material oder AntD integrieren und bietet eine intuitive API.
#Wie man React Hook Form verwendet
Installiere die Bibliothek zunächst mit npm i --save react-hook-form
Die Verwendung innerhalb deiner Komponente ist denkbar einfach: Importiere den useForm-Hook aus der Bibliothek
import { useForm } from "react-hook-form";
Der useForm-Hook gibt uns Zugriff auf viele Eigenschaften. Zunächst verwenden wir nur register und handleSubmit. Destrukturiere diese innerhalb deiner funktionalen Komponente
const { register, handleSubmit } = useForm();
Registriere ein Eingabeelement mit einem Variablennamen name wie folgt. Dadurch wird der Wert des Eingabefelds für die Formularvalidierung und -übermittlung verfügbar gemacht.
<inputclassName="border-2 outline-none p-2 rounded-md"placeholder="Name"{...register("name")}/>
Anschließend kannst du das Absenden deines Formulars auf diese Weise handhaben und eine onSubmit-Methode erstellen, die Zugriff auf die Formulardaten hat.
<form onSubmit={handleSubmit(onSubmit)}>
Nachfolgend findest du ein vollständiges Beispiel
import { useState } from "react";import { useForm } from "react-hook-form";export default function ReactHookFormMini() {const { register, handleSubmit } = useForm();const [data, setData] = useState("");const onSubmit = (data) => {setData(data)}return (<div className="w-full flex justify-center items-center bg-gray-900 p-8 border-r border-dashed"><div className="w-1/2 shadow-lg rounded-md bg-white p-8 flex flex-col" style={{height:'375px'}}><h2 className="text-center font-medium text-2xl mb-4">React Hook Form Basic</h2><formonSubmit={handleSubmit(onSubmit)}className="flex flex-1 flex-col justify-evenly"><inputclassName="border-2 outline-none p-2 rounded-md"placeholder="Name"{...register("name")}/><buttonclassName=" flex justify-center p-2 rounded-md w-1/2 self-center bg-gray-900 text-white hover:bg-gray-800"type='submit'><span>Submit</span></button></form><div className='h-4'><p> Data: {JSON.stringify(data)}</p></div></div></div>);}
So wird es aussehen. Wir haben Tailwind für das CSS verwendet, du kannst aber gerne deine eigenen CSS-Frameworks nutzen.
#Validierungen hinzufügen
Validierungen sind fester Bestandteil nahezu jedes Formulars – sie sind die erste Verteidigungslinie einer Anwendung gegen unerwünschte Daten. Validierungen stellen sicher, dass fehlerhafte Daten nicht bis zu den Backend-Servern und letztlich zu den Datenbanken vordringen. Idealerweise werden Daten in den meisten Softwaresystemen auf jeder Ebene validiert, also im Frontend, im Backend und in der Datenbank.
Bei großen Formularen kann es mühsam und repetitiv werden, Validierungen für jedes einzelne Feld sowie deren Fehlerzustände zu verwalten. React Hook Form bietet hervorragende API-Optionen und orientiert sich zudem am bestehenden HTML-Standard für die Formularvalidierung. Hier findest du die Liste der von der Bibliothek unterstützten Validierungsregeln:
required– Legt fest, ob der Eingabewert erforderlich istmin– Der minimale Wert, der akzeptiert werden kannmax– Der maximale Wert, der akzeptiert werden kannminLength– Die minimale Länge der Eingabe, die akzeptiert werden kannmaxLength– Die maximale Länge der Eingabe, die akzeptiert werden kannpattern– Ein Regex-Muster zur Validierung der Eingabevalidate– Eine beliebige benutzerdefinierte Funktion zur Validierung der Eingabe
Ein einfaches Beispiel zur Verwendung dieser Attribute
<inputclassName="border-2 outline-none p-2 rounded-md"placeholder="Phone Number"{...register("phoneNumber", {minLength: 1,maxLength: 10,})}/>
Außerdem kannst du Fehlermeldungen direkt zusammen mit der Validierungsregel übergeben, wodurch die Anzeige von Fehlern sehr einfach wird.
<inputclassName="border-2 outline-none p-2 rounded-md"placeholder="Email"{...register("email", {pattern: {value: /^.*@hygraph.com$/,message: 'Email must end with hygraph.com'}})}/>
Du kannst das Fehlerobjekt aus useFrom abrufen und die Fehlermeldung dann im Template anzeigen.
const { formState: { errors } } = useForm();
<span className="text-sm text-red-700">{errors?.email?.message}</span>
#Bibliotheken zur Schema-Validierung
React Hook Form unterstützt außerdem die Integration mit bekannten Bibliotheken zur Schema-Validierung. Du kannst Bibliotheken wie Zod, Joi oder Yup zusammen mit React Hook Form in deinen Formularen einsetzen. Schauen wir uns ein Beispiel mit der Schema-Validierungsbibliothek Yup an.
Zunächst müssen wir unser Formulardatenschema wie unten gezeigt deklarieren
// ...const schema = yup.object({fullName: yup.string().required(),age: yup.number().positive().integer().required(),}).required();// ...
Anschließend können wir den Yup-Resolver aus Yup importieren und ihn an die useForm-Methode von React Hook Form übergeben.
import { yupResolver } from "@hookform/resolvers/yup";// ...const {register,handleSubmit,formState: { errors },} = useForm({resolver: yupResolver(schema),});// ...
Hier ist ein vollständiges Beispiel
import * as yup from "yup";import { yupResolver } from "@hookform/resolvers/yup";import { useForm } from "react-hook-form";const schema = yup.object({fullName: yup.string().required(),age: yup.number().positive().integer().required(),}).required();export default function YupExample() {const {register,handleSubmit,formState: { errors },} = useForm({resolver: yupResolver(schema),});const onSubmit = (data) => console.log(data);return (<form onSubmit={handleSubmit(onSubmit)}><input {...register("fullName")} /><p>{errors.fullName?.message}</p><input {...register("age")} /><p>{errors.age?.message}</p><input type="submit" /></form>);}
#Integration mit anderen Komponenten
Es ist durchaus möglich, dass in deinem Projekt bereits UI-Frameworks eingerichtet sind und du UI-Komponenten aus dieser Bibliothek verwendest. React Hook Form lässt sich auch mit solchen UI-Frameworks integrieren. Im folgenden Beispiel siehst du, wie React Hook Form mit den Eingabekomponenten von AntD integriert werden kann.
import React, { useState } from 'react';import { Controller, useForm } from "react-hook-form";import { Input, Checkbox, Slider } from "antd";export default function RFHWithAntd() {const { control, handleSubmit } = useForm();const [data, setData] = useState({});const onSubmit = (data) => {setData(data)}return (<div className="w-full flex justify-center items-center bg-gray-900 p-8 border-r border-dashed"><div className="w-1/2 shadow-lg rounded-md bg-white p-8 flex flex-col" style={{ height: '375px' }}><h2 className="text-center font-medium text-2xl mb-4">React Hook Form With AntD</h2><formonSubmit={handleSubmit(onSubmit)}className="flex flex-1 flex-col justify-evenly"><Controllercontrol={control}name="name"render={({ field }) => (<Input{...field}placeholder="Name"/>)}/><div className="flex"><label>Is Developer?</label><Controllercontrol={control}name="isDeveloper"render={({ field: { value, onChange } }) => (<CheckboxclassName="ml-4"checked={value}onChange={(e) => {onChange(e.target.checked);}}/>)}/></div><section><label>Experience In Years</label><Controllercontrol={control}name="experience"render={({ field }) => <Slider {...field} />}/></section><buttonclassName=" flex justify-center p-2 rounded-md w-1/2self-center bg-gray-900 text-white hover:bg-gray-800"type='submit'><span>Submit</span></button></form><div className='h-4'><p> Data: {JSON.stringify(data)}</p></div></div></div>)};
#React Hook Form-Beispiele
Typescript
React Hook Form unterstützt TypeScript, was unserem Code zusätzliche Typsicherheit verleiht. Wir können die Typen für unsere Formulardaten definieren und sie wie unten gezeigt an React Hook Form übergeben
import { useForm } from "react-hook-form"type FormData = {firstName: stringage: number}export default function TypeScriptExample() {// ...const {register,setValue,handleSubmit,formState: { errors },} = useForm<FormData>()const onSubmit = handleSubmit((data) => console.log(data))return (// ...)}
Das war's schon. Jetzt weiß React Hook Form, wie unsere Formulardaten aussehen, und versucht, jegliche Typverstöße zu verhindern. Wenn wir beispielsweise den folgenden Code in das Markup des TypeScript-Formulars einfügen, zeigt es uns einen Typfehler an, der besagt, dass die Eigenschaft name im Typ FormData nicht existiert
return (// ...{errors.name?.message}// ...)
Performance-Vergleich
Wir haben bereits erwähnt, dass React Hook Form eine hervorragende Performance bietet. Einer der Gründe dafür liegt darin, wie die Bibliothek mit Re-Renders umgeht. Im Vergleich zu jeder anderen Bibliothek oder einer von Grund auf selbst implementierten Formularlösung in React führt React Hook Form kaum Re-Renders durch, wenn sich Werte innerhalb deines Formulars ändern – eine großartige Optimierung, die von Haus aus mitgeliefert wird. Wir können das anhand eines Beispiels nachvollziehen: Wir erstellen zwei Formulare mit derselben Funktionalität. Eines mit einer Implementierung von Grund auf und eines mit React Hook Form, und vergleichen anschließend die Re-Renders bei derselben Eingabe.
Normales Formular
import React, { useState } from 'react';let renderCount = 0export default function NormalForm() {const [name, setName] = useState('');const [email, setEmail] = useState('');const [showFormData, setShowFormData] = useState(false)const onSubmit = (event) => {event.preventDefault()setShowFormData(true)console.log('Run Validations Manually, maintain & show errors on UI, if all good make API call.')}renderCount += 1return (<div className="w-1/2 flex justify-center items-center bg-gray-900 p-8"><div className="w-full shadow-lg rounded-md bg-white p-8 flex flex-col" style={{ height: '375px' }}><h2 className="text-center font-medium text-2xl mb-4">Normal Form</h2>Render Count -- {renderCount}<formonSubmit={onSubmit}className="flex flex-1 flex-col justify-evenly"><inputclassName="border-2 outline-none p-2 rounded-md"placeholder="Name"value={name}onChange={(e) => { setName(e.target.value) }}/><inputclassName="border-2 outline-none p-2 rounded-md"type="email"placeholder="Email"value={email}onChange={(e) => { setEmail(e.target.value) }}/><buttonclassName=" flex justify-center p-2 rounded-mdw-1/2 self-center bg-gray-900 text-white hover:bg-gray-800"type='submit'><span>Submit</span></button></form>{<div className='h-4'><p> Data:{showFormData ?<span> {name} {email} </span>: null}</p></div>}</div></div>);}
React Hook Form
import { useState } from "react";import { useForm } from "react-hook-form";let renderCount = 0export default function ReactHookForm() {const { register, handleSubmit } = useForm();const [data, setData] = useState("");const onSubmit = (data) => {setData(JSON.stringify(data))}renderCount += 1return (<div className="w-1/2 flex justify-center items-center bg-gray-900 p-8 border-r border-dashed"><div className="w-full shadow-lg rounded-md bg-white p-8 flex flex-col" style={{ height: '375px' }}><h2 className="text-center font-medium text-2xl mb-4">React Hook Form</h2>Render Count -- {renderCount}<formonSubmit={handleSubmit(onSubmit)}className="flex flex-1 flex-col justify-evenly"><inputclassName="border-2 outline-none p-2 rounded-md"placeholder="Name"{...register("name")}/><inputclassName="border-2 outline-none p-2 rounded-md"placeholder="Email"{...register("email")}/><buttonclassName=" flex justify-center p-2 rounded-mdw-1/2 self-center bg-gray-900 text-white hover:bg-gray-800"type='submit'><span>Submit</span></button></form><div className='h-4'><p> Data: {data}</p></div></div></div>);}
Zu Beginn, ohne jegliche Eingaben im Formular.
Nach dem Eingeben derselben Werte in das Formular und dem Absenden.
React Hook Form benötigte 4 Re-Renders, während das normale Formular 58 benötigte – der Unterschied ist erheblich. Zudem handelt es sich hierbei um ein sehr einfaches Formular mit nur zwei Feldern, sodass man sich vorstellen kann, dass die Re-Renders bei einem komplexen Formular noch deutlich zahlreicher ausfallen.
Weiterführendes Beispiel
Um unser Verständnis zu vertiefen, können wir noch einen Schritt weitergehen und die übrigen verfügbaren API-Optionen erkunden. Der useForm-Hook bietet eine formState-Eigenschaft, die uns weitere Details zur Interaktion der Nutzerinnen und Nutzer mit unserem Formular liefert.
isDirty: boolescher Wert, der angibt, ob der Nutzer mit dem Formular interagiert hatisValid: boolescher Wert, der angibt, ob der aktuelle Formularzustand gültig ist.errors: ein Objekt, das Fehler enthält, falls eine der Validierungen fehlschlägt.isSubmitted: boolescher Wert, der angibt, ob der Nutzer mindestens einmal versucht hat, das Formular abzusendentouchedFields: ein Objekt, das die berührten Felder enthält.dirtyFields: ein Objekt, das die geänderten Felder enthält.
Der Unterschied zwischen dirty und touched Feldern besteht darin, dass ein Feld als touched markiert wird, sobald der Nutzer auf ein Texteingabefeld klickt – jedoch noch nicht als dirty. Erst wenn der Nutzer anschließend auch einen Wert in das Texteingabefeld eingibt, wird es als dirty markiert.
Der useForm-Hook bietet außerdem eine watch-Funktion, mit der Formulareingaben beobachtet und andere Felder dynamisch angezeigt werden können. Dynamische Formulare sind ein häufiger Anwendungsfall, bei dem Formularfelder dynamisch angezeigt werden, je nachdem, was der Nutzer in vorherige Felder eingibt.
Hier ist ein vollständiges, fortgeschrittenes Beispiel, das die Verwendung aller oben genannten Eigenschaften zeigt.
import { useState } from 'react';import { useForm } from 'react-hook-form';let renderCount = 0export default function ReactHookFormAdvanced() {const {register,handleSubmit,formState: { touchedFields, isDirty, isValid, dirtyFields, isSubmitted, errors },watch} = useForm();const [data, setData] = useState('');const watchIsDeveloper = watch('isDeveloper');renderCount += 1return (<div className='w-full flex justify-center items-center bg-gray-900 p-8'><div className='w-2/3 shadow-lg rounded-md bg-white p-8 flex flex-col justify-start' style={{ height: '700px' }}><h2 className='text-center font-medium text-2xl mb-4'>React Hook Form Advanced</h2>Render Count -- {renderCount}<formonSubmit={handleSubmit(setData)}className='flex flex-1 flex-col justify-evenly'><inputclassName='border-2 outline-none p-2 rounded-md'placeholder='Name'{...register('name')}/><inputclassName='border-2 outline-none p-2 rounded-md'placeholder='Email'{...register('email', { required: 'Email is required.' })}/><inputclassName='border-2 outline-none p-2 rounded-md'placeholder='Phone Number'{...register('phoneNumber')}/><div><span className='mr-4'>Are you a developer?</span><input type='checkbox' {...register('isDeveloper')} /></div>{watchIsDeveloper ?<div className='flex w-full '><inputclassName='flex-1 border-2 outline-none p-2 rounded-md mr-2'placeholder='Experience (Years)'{...register('exp_years')} /><inputclassName='flex-1 border-2 outline-none p-2 rounded-md'placeholder='Experience (Months)'{...register('exp_months')} /></div>: null}<buttonclassName=' flex justify-center p-2 rounded-mdw-1/2 self-center bg-gray-900 text-white hover:bg-gray-800'type='submit'><span>Submit</span></button></form><p className='w-4/5'> <strong> Data: </strong> {JSON.stringify(data)} </p><p> <strong> Is Valid: </strong> {JSON.stringify(isValid)}</p><p> <strong> Is Dirty : </strong> {JSON.stringify(isDirty)} </p><p> <strong> Is Submited: </strong> {JSON.stringify(isSubmitted)}</p><p> <strong> Errors: </strong> {JSON.stringify(errors?.email?.message)}</p><p> <strong> Dirty Fields : </strong> {JSON.stringify(dirtyFields)} </p><p> <strong> Touched Fields: </strong> {JSON.stringify(touchedFields)} </p><p> <strong> Watching Is Developer: </strong> {JSON.stringify(watchIsDeveloper)}</p></div></div>);}
Blog Author