Published on 12.9.2020
Forms in React are often a source of frustration, particularly when dealing with large forms or complex validation logic. By default, React’s form management requires a lot of boilerplate code, especially when using controlled inputs with useState. Enter React Hook Form—a library designed to simplify form handling while offering better performance and a cleaner code structure.
In this guide, we’ll explore why React Hook Form is a better solution than traditional controlled inputs, how to build dynamic forms with validation, and how to integrate Yup for schema-based validation.
Why React Hook Form is Better Than Controlled Inputs with useState
When building forms in React, developers often rely on controlled components, where each input field’s value is tied to the state of the component using the useState
hook. While this approach works fine for small forms, it can become cumbersome and inefficient when dealing with larger or more dynamic forms.
The Problem with Controlled Inputs
With controlled components, each form input requires a separate piece of state. For example:
import React, { useState } from 'react';
function MyForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
console.log({ name, email });
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<button type="submit">Submit</button>
</form>
);
}
While this works, there are a few downsides:
- Boilerplate code: You need to manually create state for every field and manage updates.
- Performance issues: Each keystroke triggers a re-render of the form, which can be problematic with large forms.
- Validation complexity: Handling validation logic manually can lead to cluttered and difficult-to-maintain code.
Enter React Hook Form
React Hook Form aims to solve these issues by removing the need for manually managing input state and re-renders. It minimizes re-renders by only tracking changes to the form data, not every individual input field. It also provides a simple API for form validation and dynamic form creation.
Here’s how you can use React Hook Form to simplify the previous example:
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('name')}
placeholder="Name"
/>
<input
{...register('email')}
placeholder="Email"
/>
<button type="submit">Submit</button>
</form>
);
}
In this example, useForm()
provides an API for registering inputs and handling form submission. The register
function links input fields to the form state, and handleSubmit
handles the form submission. The best part? No need to manage state manually.
Building Dynamic Forms with Validation
React Hook Form supports dynamic forms, where form fields can be added or removed based on user interactions. You can also add validation rules directly in the register()
method or by integrating with external libraries.
Here’s an example of a dynamic form with validation:
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
function DynamicForm() {
const { register, handleSubmit, setValue, getValues, formState: { errors } } = useForm();
const [fields, setFields] = useState([{ name: '' }]);
const onSubmit = (data) => {
console.log(data);
};
const addField = () => {
setFields([...fields, { name: '' }]);
};
const removeField = (index) => {
const updatedFields = [...fields];
updatedFields.splice(index, 1);
setFields(updatedFields);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{fields.map((field, index) => (
<div key={index}>
<input
{...register(`field-${index}`, { required: 'This field is required' })}
placeholder={`Field ${index + 1}`}
/>
{errors[`field-${index}`] && <p>{errors[`field-${index}`].message}</p>}
<button type="button" onClick={() => removeField(index)}>
Remove
</button>
</div>
))}
<button type="button" onClick={addField}>
Add Field
</button>
<button type="submit">Submit</button>
</form>
);
}
In this example:
- We use the
useState
hook to maintain a list of dynamic fields. - Each field is registered with React Hook Form, and validation is added with the
required
rule. - Fields can be dynamically added and removed using the
addField
andremoveField
functions. - Validation errors are displayed if a required field is left empty.
Integrating with Yup for Schema-Based Validation
One of the standout features of React Hook Form is its ability to integrate easily with Yup, a powerful schema validation library. With Yup, you can define complex validation logic in a single schema and pass it to React Hook Form for automatic validation.
Here’s an example of how to integrate Yup for schema-based validation:
- Install Yup and @hookform/resolvers:
npm install yup @hookform/resolvers
- Create a validation schema with Yup:
import * as Yup from 'yup';
const validationSchema = Yup.object({
name: Yup.string().required('Name is required'),
email: Yup.string().email('Invalid email').required('Email is required'),
});
- Integrate Yup with React Hook Form:
import React from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as Yup from 'yup';
const validationSchema = Yup.object({
name: Yup.string().required('Name is required'),
email: Yup.string().email('Invalid email').required('Email is required'),
});
function MyForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: yupResolver(validationSchema),
});
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<input
{...register('name')}
placeholder="Name"
/>
{errors.name && <p>{errors.name.message}</p>}
</div>
<div>
<input
{...register('email')}
placeholder="Email"
/>
{errors.email && <p>{errors.email.message}</p>}
</div>
<button type="submit">Submit</button>
</form>
);
}
In this example, we:
- Define a Yup validation schema that checks for required fields and email format.
- Use
yupResolver
from@hookform/resolvers
to pass the validation schema into React Hook Form. - Display validation errors next to the input fields if they fail the schema validation.
Conclusion
React Hook Form simplifies form handling by reducing boilerplate code, improving performance, and making form validation easy to manage. Its integration with Yup allows for clean, schema-based validation, and dynamic form support is intuitive and simple. By adopting React Hook Form, you can create more efficient, maintainable, and scalable forms in your React projects.
Give React Hook Form a try and see how much easier and cleaner managing forms can be!