React Hook Form

Form

  • Mainly responsible for defining form submitting function, the type of form data, validation rule of each component, pass the props to component through form context provider

  • List of validation rules supported:

    • required

    • min

    • max

    • minLength

    • maxLength

    • pattern => regExp

    • validate => using self-defined function to validate

  • To support schema-based form validation with Yup, Zod , Superstruct & Joi, where you can pass your schema to useForm as an optional config. It will validate your input data against the schema and return with either errors or a valid result.

import React from "react";
import { FormProvider, useForm } from "react-hook-form";
import { Form } from "react-aria-components";
import CustomTextField from "../forms/customTextField";
import CustomButton from "../forms/customButton";
import useLoginMutation from "../../hooks/login/useLoginMutation";
import { useNavigate } from "@tanstack/react-router";
import { zodResolver } from "@hookform/resolvers/zod";
import { LoginRequest, loginRequestSchema } from "../../types/login";

const defaultFormValues: LoginRequest = {
  email: "",
  password: "",
};

const Login = () => {
  const { mutateAsync } = useLoginMutation();
  const navigate = useNavigate();
  const methods = useForm({
    defaultValues: defaultFormValues,
    resolver: zodResolver(loginRequestSchema),
  });

  const { handleSubmit, setError } = methods;

  const onSubmit = async (data: LoginRequest) => {
    try {
      const user = await mutateAsync(data);
      localStorage.setItem("user", user.id.toString());
      navigate({ to: "/" });
    } catch (err) {
      if (err instanceof Error) {
      // set back the error to the field
        setError("email", { message: err.message });
      }
    }
  }
  // to have side effect when failed to submit
  const onError = (input: unknown) => {
    console.log(input);
  };
  
  return (
    <FormProvider {...methods}>
      <Form
        className="h-full flex flex-col items-center justify-center gap-2"
        onSubmit={(e) => {
          e.preventDefault();
          // for handle submit, the validation will be triggered when the data is
          // firstly submitted or changed
          handleSubmit(onSubmit, onError)();
        }}
      >
        <CustomTextField
          name="email"
          rules={{
            required: true,
          }}
          label={"Email"}
        />
        <CustomTextField
          name="password"
          type="password"
          rules={{ required: true }}
          label={"Password"}
        />
        <CustomButton label="Login" type="submit" className="mt-5" />
      </Form>
    </FormProvider>
  );
};

export default Login;

Component

  • Component is mainly responsible to handle field status correctly and the change behaviour

  • controlis passed into component with provider by default and linked with the parent form, so that the component becomes controllable

  • useControllerreturn the field status (isDirty, error) and on change method, etc

  • For array of field case, using useFieldArray hook to handle

  • Return the values of array and append, remove method

Last updated

Was this helpful?