React Hook Form

Loading

React Hook Form is a modern, performant library for building forms in React with minimal re-renders and maximum flexibility. Here’s everything you need to know:

Core Concepts

1. Basic Usage

import { useForm } from 'react-hook-form';

function BasicForm() {
  const { 
    register,
    handleSubmit,
    formState: { errors }
  } = useForm();

  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input 
        {...register("firstName", { required: true })}
        placeholder="First name"
      />
      {errors.firstName && <p>First name is required</p>}

      <input
        {...register("lastName", { required: "Last name is required" })}
        placeholder="Last name"
      />
      {errors.lastName && <p>{errors.lastName.message}</p>}

      <button type="submit">Submit</button>
    </form>
  );
}

2. Validation Schema (Yup Integration)

import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';

const schema = yup.object().shape({
  email: yup.string().email().required(),
  age: yup.number().positive().integer().required(),
});

function ValidationForm() {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: yupResolver(schema),
  });

  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("email")} placeholder="Email" />
      {errors.email && <p>{errors.email.message}</p>}

      <input {...register("age")} placeholder="Age" type="number" />
      {errors.age && <p>{errors.age.message}</p>}

      <button type="submit">Submit</button>
    </form>
  );
}

Advanced Features

1. Dynamic Forms

function DynamicForm() {
  const { control, handleSubmit } = useForm();

  const { fields, append, remove } = useFieldArray({
    control,
    name: "items"
  });

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      {fields.map((field, index) => (
        <div key={field.id}>
          <Controller
            name={`items.${index}.value`}
            control={control}
            defaultValue={field.value}
            render={({ field }) => <input {...field} />}
          />
          <button type="button" onClick={() => remove(index)}>
            Remove
          </button>
        </div>
      ))}

      <button 
        type="button" 
        onClick={() => append({ value: '' })}
      >
        Add Item
      </button>

      <button type="submit">Submit</button>
    </form>
  );
}

2. File Uploads

function FileUploadForm() {
  const { register, handleSubmit } = useForm();

  return (
    <form onSubmit={handleSubmit(data => {
      const formData = new FormData();
      formData.append("file", data.file[0]);
      // Submit to API
    })}>
      <input type="file" {...register("file")} />
      <button type="submit">Upload</button>
    </form>
  );
}

3. Conditional Fields

function ConditionalForm() {
  const { register, watch } = useForm();
  const showAddress = watch("showAddress");

  return (
    <form>
      <label>
        <input type="checkbox" {...register("showAddress")} />
        Show address fields
      </label>

      {showAddress && (
        <>
          <input {...register("address.street")} placeholder="Street" />
          <input {...register("address.city")} placeholder="City" />
        </>
      )}
    </form>
  );
}

Performance Optimization

1. Isolate Re-renders

const Input = React.memo(({ register, name, ...rest }) => (
  <input {...register(name)} {...rest} />
));

function OptimizedForm() {
  const { register } = useForm();

  return (
    <form>
      <Input register={register} name="firstName" placeholder="First name" />
      <Input register={register} name="lastName" placeholder="Last name" />
    </form>
  );
}

2. Debounced Inputs

function DebouncedForm() {
  const { register, watch } = useForm();
  const searchTerm = watch("search");
  const [debouncedTerm] = useDebounce(searchTerm, 500);

  useEffect(() => {
    if (debouncedTerm) {
      // Perform search
    }
  }, [debouncedTerm]);

  return (
    <form>
      <input {...register("search")} placeholder="Search..." />
    </form>
  );
}

Integration Patterns

1. With UI Libraries

function MuiForm() {
  const { control } = useForm();

  return (
    <Controller
      name="muiSelect"
      control={control}
      defaultValue=""
      render={({ field }) => (
        <Select {...field}>
          <MenuItem value="option1">Option 1</MenuItem>
          <MenuItem value="option2">Option 2</MenuItem>
        </Select>
      )}
    />
  );
}

2. With State Management

function ZustandForm() {
  const { user, updateUser } = useUserStore();
  const { handleSubmit, register } = useForm({
    defaultValues: user
  });

  return (
    <form onSubmit={handleSubmit(updateUser)}>
      <input {...register("name")} />
      <input {...register("email")} />
    </form>
  );
}

Best Practices

  1. Default Values: Always provide default values for controlled components
   useForm({
     defaultValues: {
       firstName: '',
       lastName: ''
     }
   });
  1. Error Handling: Display helpful error messages
   <input
     {...register("age", {
       validate: value => value > 18 || "Must be adult"
     })}
   />
  1. Form Reset: Clean form after submission
   const { reset } = useForm();
   const onSubmit = async data => {
     await submitToAPI(data);
     reset();
   };
  1. Accessibility: Ensure proper labeling
   <label htmlFor="email">Email</label>
   <input id="email" {...register("email")} />
  1. Type Safety: Use TypeScript for better developer experience
   interface FormValues {
     email: string;
     password: string;
   }

   const { register } = useForm<FormValues>();

React Hook Form provides excellent performance out of the box by isolating re-renders and minimizing validation overhead. For most forms, it’s the most efficient solution available in the React ecosystem.

Leave a Reply

Your email address will not be published. Required fields are marked *