reactjsformsreact-hook-form

reset() issue of react-form-hook


I have created a form using react-hook-form. I have used zustand for storing form details as a cache in local storage, so that if the user wants to fill in details of the form from a previously filled form.

Functionality

Issue

Normally when I filling my details and submit my form I get submitted and all fields are cleared. But when I import data from the cache and submit form then form is getting submitted but the form fields are not getting cleared. I am unable to figure out why this is happening.

Code

import React, { useEffect } from 'react';
import { useForm, Controller } from 'react-hook-form';
import { DatePicker } from '@/components/date-picker';
import { RiLoader5Fill } from 'react-icons/ri';
import { useJobPostCache } from '@/store/jobpost-cache';

//shadcn UI imports
import { Textarea } from '@/components/ui/textarea';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';

/**
 *
 * @description This is a form to post a job. It uses the react-hook-form library for form validation.
 */
function JobPost() {
  const {
    formState: { errors, isSubmitting, isSubmitSuccessful },
    register,
    handleSubmit,
    control,
    reset,
  } = useForm();

  const { jobPostCache, setJobPostCache, clearJobPostCache } =
    useJobPostCache();

  const formSumbimission = async (data) => {
    setJobPostCache(data);
    await new Promise((resolve) => setTimeout(resolve, 2000));
  };

  const importFormDataFromCache = () => {
    console.log('importing data from cache');
    reset(jobPostCache);
  };

  useEffect(() => {
    if (isSubmitSuccessful) {
      console.log('clearing form');
      reset();
    }
  }, [isSubmitSuccessful]);

  return (
    <div>
      <div className="max-w-[1300px]  mx-auto min-h-screen px-3">
        <h1 className="text-center text-2xl pt-10 font-semibold ">
          Post your <span className="text-blue-500 font-semibold">JOB</span>!
        </h1>
        {
          <p
            className="text-sm text-right text-red-500 cursor-pointer"
            onClick={importFormDataFromCache}
          >
            Import Data from previous Job post
          </p>
        }
        <form
          onSubmit={handleSubmit(formSumbimission)}
          className="mx-auto max-w-[900px]"
        >
          <div className="block sm:flex gap-6">
            <div className="flex flex-col sm:w-1/2">
              <h2 className="font-semibold mt-7">Job Title</h2>
              <Input
                type="text"
                placeholder="Job Title"
                {...register('job-title', {
                  required: 'Job title is required',
                })}
              />
              <span className="text-red-500 mt-2">
                {errors['job-title'] && errors['job-title'].message}
              </span>
            </div>
            <div className="flex flex-col sm:w-1/2">
              <h2 className="font-semibold mt-7">Company Name</h2>
              <Input
                type="text"
                placeholder="Company Name"
                {...register('company-name', {
                  required: 'Company Name is required',
                })}
              />
              <span className="text-red-500 mt-2">
                {errors['company-name'] && errors['company-name'].message}
              </span>
            </div>
          </div>
          <div className="block sm:flex gap-6 ">
            <div className="flex flex-col sm:w-1/2">
              <h2 className="font-semibold mt-7">Job Type</h2>
              <Controller
                name="jobType"
                control={control}
                rules={{ required: 'Please select job type' }}
                render={({ field }) => (
                  <Select value={field.value} onValueChange={field.onChange}>
                    <SelectTrigger className="w-full">
                      <SelectValue placeholder="Job type" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="full-time">Full Time</SelectItem>
                      <SelectItem value="part-time">Part Time</SelectItem>
                      <SelectItem value="internship">Internship</SelectItem>
                    </SelectContent>
                  </Select>
                )}
              />
              <span className="text-red-500 mt-2">
                {errors['jobType'] && errors['jobType'].message}
              </span>
            </div>
            <div className="flex flex-col sm:w-1/2">
              <h2 className="font-semibold mt-7">Location</h2>
              <Input
                type="text"
                placeholder="Locatione"
                {...register('location', {
                  required: 'Location is required',
                })}
              />
              <span className="text-red-500 mt-2">
                {errors['location'] && errors['location'].message}
              </span>
            </div>
          </div>
          <div className="block sm:flex gap-6">
            <div className="flex flex-col sm:w-1/2">
              <h2 className="font-semibold mt-7">Salary Range</h2>
              <Input
                type="text"
                placeholder="Salary Range"
                {...register('salary', {
                  required: 'Salary range is required',
                })}
              />
              <span className="text-red-500 mt-2">
                {errors['salary'] && errors['salary'].message}
              </span>
            </div>
            <div className="flex flex-col sm:w-1/2">
              <h2 className="font-semibold mt-7">Application Deadline</h2>
              <div className="flex flex-col">
                <Controller
                  name="application-deadline"
                  control={control}
                  rules={{ required: 'Please select application deadline' }}
                  render={({ field }) => (
                    <DatePicker
                      selected={field.value}
                      onChange={field.onChange}
                    />
                  )}
                />
                {errors['application-deadline'] && (
                  <span className="text-red-500 mt-2">
                    {errors['application-deadline'].message}
                  </span>
                )}
              </div>
            </div>
          </div>
          <h2 className="font-semibold mt-7">Job Description</h2>
          <Textarea
            placeholder="Job Description"
            {...register('job-description', {
              required: 'Job description is required',
              minLength: {
                value: 30,
                message: 'Job description must be atleast 30 characters',
              },
            })}
          />
          <span className="text-red-500 mt-2">
            {errors['job-description'] && errors['job-description'].message}
          </span>
          <div className="block sm:flex gap-6">
            <div className="flex flex-col sm:w-1/2">
              <h2 className="font-semibold mt-7">Contact email</h2>
              <Input
                type="email"
                placeholder="Contact email"
                {...register('email', {
                  required: 'Please enter an email address',
                  validate: (value) => {
                    return (
                      /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/.test(
                        value
                      ) || 'Invalid email address'
                    );
                  },
                })}
              />
              <span className="text-red-500 mt-2">
                {errors['email'] && errors['email'].message}
              </span>
            </div>
            <div className="flex flex-col sm:w-1/2">
              <h2 className="font-semibold mt-7">Contact Phone Number</h2>
              <Input
                type="tel"
                placeholder="Contact phone number"
                {...register('phone-number', {
                  minLength: {
                    value: 10,
                    message: 'Phone number must be atleast 10 digits',
                  },
                  maxLength: {
                    value: 10,
                    message: 'Phone number must not be more than 10 digits',
                  },
                })}
              />
              <span className="text-red-500 mt-2">
                {errors['phone-number'] && errors['phone-number'].message}
              </span>
            </div>
          </div>
          <div className="flex justify-center items-center ">
            <Button className="mt-7 w-full sm:w-36 mb-5">
              {isSubmitting ? (
                <div className="animate-spin text-3xl">
                  <RiLoader5Fill />
                </div>
              ) : (
                'Post Job'
              )}
            </Button>
          </div>
        </form>
      </div>
    </div>
  );
}

export default JobPost;

Solution

  • Reason

    Its not mentioned in reset() documentation of react-hook-form, that if somehow we are providing/setting field values through reset at starting then reset() function considers that value as default value.

    What happened in code

    When data was filled with cache at starting with help of reset(), then reset considered it as default value of fields and at the time of submission of form reset() was called, so it populated all the fields with same value as submitted recently.

    Solution

    Just need to provide whole object as key value with empty values while reseting form after submission.

    useEffect(() => {
        if (isSubmitSuccessful) {
          console.log('clearing form');
          reset({
            'application-deadline': '',
            'company-name': '',
            email: '',
            'job-description': '',
            'job-title': '',
            jobType: '',
            location: '',
            'phone-number': '',
            salary: '',
          });
        }
      }, [isSubmitSuccessful]);