2024-02-14 |

Bypassing Formik Yup Validation

I’ve been using Formik with Yup validations for a while and it really a pretty good experience. The integration is tight and really all one needs to do is to define the validation schema and then pass it to Formik via the validationSchema. The validation is then run on every change to the form and the errors are displayed.

const schema = Yup.object().shape({
    restricted: Yup.bool().required("Required"),
    payer: Yup.string().required("Required"),
    feeSchedule: Yup.string().required("Required")
    ...
  });

  return (
    <Formik validationSchema={schema}
    onSubmit={handleSubmit}
    initialValues={{
        restricted: false,
        payer: "",
        feeSchedule: ""
    }}>
        ...
        <FormikSubmitButton label="Save"/>
    </Formik>
  )

But today’s challenge was that we had a request to show validation errors but still submit the form. I have a custom submit button control I use which hooks into the Formik context and pulls out the submitForm and isValid properties. It then disables the button if the form isn’t valid. This means that I can have a really consistent look and feel to my forms’ submit buttons.

It looks a bit like this

const FormikSubmitButton = (props) => {
  const { label, sx, disabled, ...restProps } = props;

  const { submitForm, isValid } = useFormikContext();

  const isDisabled = (!isValid || disabled);

  return (
    <>
      <Button
        disabled={isDisabled}
        onClick={submitForm}
        variant="contained"
        color="primaryAction"
        sx={sx}
        {...restProps}
      >
        {label}
      </Button>
    </>
  );
};

So the button disabled itself if the form isn’t valid. Now instead we need to bypass this so I passed in a new parameter allowInvalid which would allow the button to be clicked even if the form wasn’t valid.

const FormikSubmitButton = (props) => {
  const { label, sx, disabled, allowInvalid, ...restProps } = props;

  const { submitForm, isValid } = useFormikContext();

  const isDisabled = (!isValid || disabled) && !allowInvalid;

  return (
    <>
      <Button
        disabled={isDisabled}
        onClick={submitForm}
        variant="contained"
        color="primaryAction"
        sx={sx}
        {...restProps}
      >
        {label}
      </Button>
    </>
  );
};

This fixes the button disabling itself but it doens’t resolve the issue of the form not submitting. The submitForm function from Formik will not submit the form if it is invalid. To get around this I had to call the handleSubmit function from the form and then manually set the form to be submitted. This is enough of a change that I wanted a whole separate component for it.

const FormikValidationlessSubmitButton = (props) => {
  const { label, sx, disabled, onSubmit, ...restProps } = props;

  const { setSubmitting, values } = useFormikContext();

  const handleSubmit(){
    setSubmitting(true);
    if(onSubmit){
      onSubmit(values);
    }
    setSubmitting(false);
  }

  return (
    <>
      <Button
        disabled={disabled}
        onClick={handleSubmit}
        variant="contained"
        color="primaryAction"
        sx={sx}
        {...restProps}
      >
        {label}
      </Button>
    </>
  );
};

This component can then be used in place of the FormikSubmitButton when you want to bypass validation.

<Formik validationSchema={schema}
    onSubmit={handleSubmit}
    initialValues={{
        restricted: false,
        payer: "",
        feeSchedule: ""
    }}>
        ...
        <FormikValidationlessSubmitButton onSubmit={handleSubmit} label="Save"/>
    </Formik>