Search code examples
reactjsvalidationformikyup

Formik and yup validation is validating a non-required file input field


I have an input field (type file) that for some reason validates even though is not required. With serial number i have a validation and it is not required and seems to work fine, no errors when submitting the empty field.. For the file input field I want only the input field to validate when a file is being uploaded. What am i doing wrong?, i get this(see image below) when i click submit to test the validation.

here's my code:

const validationSchema = Yup.object({
    title: Yup.string()
      .required("Title is required")
      .min(8, "Title must be at least 8 characters")
      .max(100, "Title cannot be more than 100 characters"),
    description: Yup.string()
      .required("Description is required")
      .min(8, "Description must be at least 8 characters")
      .max(100, "Description cannot be more than 100 characters"),
    serialNumber: Yup.string()
      .min(4, "Serial number must be at least 4 characters")
      .max(16, "Serial number cannot be more than 16 characters"),
    productStatus: Yup.number().required("A Status is required"),
    productType: Yup.number().required("A type is required"),
    img: Yup.mixed()
      .test(
        "fileSize",
        "File size too large, max file size is 1 Mb",
        (file) => file && file.size <= 1100000
      )
      .test(
        "fileType",
        "Incorrect file type",
        (file) =>
          file && ["image/png", "image/jpg", "image/jpeg"].includes(file.type)
      ),
  });
{formik.errors["img"] && formik.touched["img"] && (
                  <div style={{ color: "#B2484D", fontSize: ".8rem" }}>
                    {formik.errors.img}
                  </div>
                )}

           <Field name="img">
                  {() => {
                    // < - this will take fieldProps.. not used in this case, using formik instead
                    return (
                      <>
                        <input
                          onBlur={formik.handleBlur}
                          onChange={({ currentTarget }) => {
                            const file = currentTarget.files![0];
                            const reader = new FileReader();

                            if (file) {
                              reader.onloadend = () => {
                                setSelectedFile({
                                  file,
                                  previewURI: reader.result!,
                                });

                                setuploadbtnLabel(
                                  `You selected: ${file.name} - click again to change`
                                );
                              };
                              reader.readAsDataURL(file);
                              formik.setFieldValue("img", file);
                            }
                          }}
                          ref={inputFile}
                          type="file"
                          style={{ display: "none" }}
                          accept=".png,.jpg,.jpeg"
                        />
                      </>
                    );
                  }}
                </Field>

EDIT: here's a codesandbox https://codesandbox.io/s/thirsty-chandrasekhar-34q1q?file=/src/App.js

enter image description here


Solution

  • I think you should try to add notRequired() or even nullable() to the img field on the YUP schema definition. From Yup docs:

    Mark the schema as not required. Passing undefined (or null for nullable schema) as value will not fail validation.

    Edit: Based on the sandbox you provided I been able to figure it out. The problem comes from the test you added. Basically the issue is that file is undefined when no file is selected so the validation always fails.

    For example you could change this:

     .test(
        "fileSize",
        "File size too large, max file size is 1 Mb",
        (file) => file && file.size <= 1100000
      )
    

    To this:

    .test(
        "fileSize",
        "File size too large, max file size is 1 Mb",
        (file) => {
          if (file) {
            return file.size <= 1100000;
          } else {
            return true;
          }
        }
      )
    

    notRequired() or nullable() are not even necessary to make it work the way you expect to. Here is the link to the sandbox with this fix. If that works for you please do mark the answer as accepted.

    Good luck!