Handling Forms in React Using React Hook Form
Introduction
Handling forms in react is a tedious task, even for a small form the state management, validation, loading state etc. results in increase in number of lines for the code. And if the form is bigger these issues grow exponentially. That’s where the react hook form comes in. React Hook Form is a package used to handle the whole functionality of a form. It simplifies the task of working with forms drastically.
Problem
Consider the below native react form.
import { useState } from "react";
export const ReactHookSample = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [errors, setErrors] = useState<{ email: string; password: string }>({
email: "",
password: "",
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setErrors({ email: "", password: "" });
// Manual Validation
if (!email.includes("@")) {
setErrors({ ...errors, email: "Email must include @" });
return;
}
if (password.length < 8) {
setErrors({
...errors,
password: "Password must be atleast 8 characters",
});
return;
}
console.log("Form Submitted");
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
{errors.email && <div>{errors.email}</div>}
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{errors.password && <div>{errors.password}</div>}
<button type="submit">Submit</button>
</form>
);
};
The form seems simple, and it would work fine, so what is the issue you might ask. Yes, it works fine. But this is just a basic form, what if the fields are increased?
The states to manage those fields will be increased and also the validation state must contain those fields if required. And then the validation for those fields should also be added. So, the simple form can become complex very quickly.
Solution & Implementation
Now let's check this scenario in react-hook-form :
import type { SubmitHandler } from "react-hook-form";
import { useForm } from "react-hook-form";
type FormFields = {
email: string;
password: string;
};
export const ReactHookSample = () => {
const { register, handleSubmit } = useForm<FormFields>();
const onSubmit: SubmitHandler<FormFields> = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("email")} type="text" placeholder="Email" />
<input {...register("password")} type="password" placeholder="Password" />
<button type="submit">Submit</button>
</form>
);
};
The form seems much simpler and more readable.
Handling Input Data and Submission:
Form Validation:
<input
{...register("password", {
required: "Password is required",
minLength: 8,
})}
type="password"
placeholder="Password"
/>
{errors.password && <div>{errors.password.message}</div>}
const {
formState: { errors },
} = useForm<FormFields>();
Handling Loading State:
const {
formState: { isSubmitting },
} = useForm<FormFields>();
<button disabled={isSubmitting} type="submit">
{isSubmitting ? "Loading..." : "Submit"}
</button>
Handling Backend Errors:
const {
handleSubmit,
setError,
} = useForm<FormFields>();
const onSubmit: SubmitHandler<FormFields> = (data) => {
try {
console.log(data);
// sending the data to backend
} catch {
setError("root", { message: "Email is already taken" });
}
};
Bonus: Implementation of Zod with React Hook Form
By implementing zod we can even reduce the lines of code by using zod for validation.
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
type FormFields = z.infer<typeof schema>;
export const ReactHookSample = () => {
const {
register,
handleSubmit,
} = useForm<FormFields>({
resolver: zodResolver(schema),
});
<input
{...register("password", {
required: "Password is required",
minLength: 8,
})}
type="password"
placeholder="Password"
/>
// the validation done here can be removed.
// So, the input tag would be like this
<input {...register("password")} type="password" placeholder="Password" />
Conclusion
By using react-hook-form the handling of form functionalities can be simplified resulting in lesser lines of code, improved efficiency and a strict coding standard across all forms in the application.