testMay 27, 2024

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.

  • UseForm - is the hook from react-hook-form library for which data type is declared by FormFields.
  • Handling Input Data and Submission:

  • Register function - is used to connect the input fields to the react-hook-form and store the data.
  • handleSubmit function - is used to prevent default behavior of the form and to do validity checks before actually passing onto onSubmit function.
  • Form Validation:

  • For the form validation an object can be passed as a parameter for the register function, i.e.:
  •  <input
            {...register("password", {
              required: "Password is required",
              minLength: 8,
            })}
            type="password"
            placeholder="Password"
          />
          {errors.password && <div>{errors.password.message}</div>}
  • These are the list of validation rules supported.
  • The error state can be extracted from formState property of react-hook-form , i.e.
  • const {
        formState: { errors },
      } = useForm<FormFields>();

    Handling Loading State:

  • The isSubmitting property in formState returns a boolean when the form is submitting making it easy for the programmer to handle loading state.
  • const {
        formState: { isSubmitting },
      } = useForm<FormFields>();
     <button disabled={isSubmitting} type="submit">
            {isSubmitting ? "Loading..." : "Submit"}
          </button>
  • For more details click here.
  • Handling Backend Errors:

  • The setError function can be used to store the error thrown from backend when submitting the form.
  • 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" });
        }
      };
  • For more details click here.
  • 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.

    Reference Links

  • https://react-hook-form.com/docs
  • React Hook Form - Complete Tutorial (with Zod) (youtube.com)