Validating an optional Field With React Hook Form

232 Views Asked by At

I'm working on a form with some optional fields that need to be validated only if an input is entered. However, react hook form returns a validation error even when no inputs. I have also tried to set:

required: {
value: false,
message: "This field is not required"
}

But it still returns a validation error even when no inputs are entered.

Below is the code snippets for one of the fields that need to be validated only if an input is entered.

<div className={styles.formInputFile}>
          <label htmlFor="ref_letter">
            Reference Letter (For Doctorate and Diploma Courses)
          </label>
          <Controller
            control={control}
            name={"reference_letter"}
            rules={{
            required: {
              value: refLetterRequired();
              message: "This is field is required",
},
              validate: {
                fileSize: (file) => {
                  return (
                    file?.size < 2000100 &&
                    "File size shouldn't be more than 2MB!"
                  );
                },
                acceptedFormats: (file) => {
                  return (
                    ["image/png", "image/jpeg", "application/pdf"].includes(
                      file?.type
                    ) || "Only JPEG, PNG and PDF files are accepted!"
                  );
                },
              },
            }}
            render={({ field: { value, onChange, ...field } }) => {
              return (
                <input
                  {...field}
                  value={value?.fileName}
                  onChange={(event) => {
                    onChange(event.target.files[0]);
                  }}
                  type="file"
                  id="rererence_letter"
                  accept=".png, .jpeg, .pdf"
                />
              );
            }}
          />
          <p>{errors.reference_letter?.message}</p>
        </div>

Second Edit:

The form is a multi-step form and each step needs to be free of errors before users can move to the next step. Below are all the code snippets controlling the field. I keep getting validation errors even when the right inputs are there and when the field is not required.

  const certCourses = [
    "Certificate in D",
    "Certificate in t",
    "certificate in H",
    "Certificate in P",
    "Certificate in Pus",
    "Certificate Hec Diseases",
    "Certificate in He",
  ];

  const refLetterRequired = () => {
    for (let i = 0; i < certCourses.length; i++) {
      if (watch("proposed_course") === certCourses[i]) return false;
    }
    return true;
  };

Expectations. If the "proposed_course" selected is not in "certCourses", then the field should be required, otherwise, the field should not be required.

Code controlling "next" button of the multi-step form:

  const next = async (e) => {
    const field = steps[currentStep].fields;
    const output = await trigger(field);
    if (!output) return;
    setValue(e.target.name, e.target.value, {
      shouldDirty: true,
      shouldValidate: true,
      shouldTouch: true,
    });
    setCurrentStep(currentStep + 1);
  };

const [currentStep, setCurrentStep] = useState(0);




const steps = [
  {
    name: "step 1",
    description: "Personal Information",
    fields: [
      "title",
      "surname",
      "first_name",
      "gender",
      "middle_name",
      "maiden_name",
      "religion",
      "birth_date",
      "nationality",
      "state_of_origin",
      "disability",
    ],
  },
  {
    name: "step 2",
    description: "Next of Kin Information",
    fields: [
      "next_of_kin_name",
      "next_of_kin_address",
      "next_of_kin_relationship",
      "next_of_kin_phone",
      "next_of_kin_occupation",
    ],
  },
}
2

There are 2 best solutions below

0
Batemir Karuev On BEST ANSWER

You can check if the field value is empty or equal to the default value and return "true"(i.e. indicate that the field passed validation) in that case.
For example

fileSize: (file) => {
  if (file === null) return true;
  return (
    file?.size < 2000100 &&
    "File size shouldn't be more than 2MB!"
   );
},

There is a closed issue about this in the react-hook-form github https://github.com/react-hook-form/react-hook-form/issues/1781

6
Kyle Xyian Dilbeck On

You can get your desired functionality by using watch and setting conditional rules. here is a basic example of doing this, copy this and try it out in a sandbox, it only requires the second input when the first input is given.

import { useEffect } from "react";
import { useForm } from "react-hook-form";

type FieldInputs = {
  requiredInput: string;
  optionalInput: string;
};

export const WatchExample = () => {

  const { watch, register } = useForm<FieldInputs>();
  const HasInput = watch("requiredInput");

  return (
    <div>
      <input {...register("requiredInput", { required: true })} />
      <input
        type="text"
        {...register("optionalInput", {
          required: HasInput?.length > 1 ? true : false,
        })}
      />
      {HasInput?.length > 1 && <div>Only show when input is given </div>}
    </div>
  );
};