0
votes

I have a multistep form, and I keep each step under a single <Formik /> tag. The initial values and validation schemas are kept in objects for each step:

const initialValues = {
  step1Values: { ...values1 },
  step2Values: { ...values2 },
  step3Values: { ...values3 },
}

const validationSchema = Yup.object().shape({
  step1Schemas: { ...yupSchema1 },
  step2Schemas: { ...yupSchema2 },
  step3Schemas: { ...yupSchema3 },
});

// in component render/return:

<Formik
  initialValues={initialValues}
  validationSchema={validationSchema}
  validateOnMount
  validateOnBlur
/>

Each step has its own component rendered within the Formik tag, and use useFormikContext to grab values, errors, i.e. with const formikProps = useFormikContext(), then for a given value of a field, I can say:

// in step 1

<input
  name="step1Values.someValue"
  value={step1Values.someValue}
  onChange={formikProps.handleChange}
/>

I want all components under the same form umbrella for persistence purposes (form values will persist even as they change from one step to another). However, there are certain cases where the user can skip step one or two and not fill them out, and jump to step 3 (based on state of the redux-store). Submitting the form is only dependent on step 3 being properly validated according to formik.

I have internal checks in components 1 and 2 to move through those steps, but I want formik to track the errors to show errors if the fields are empty after having been touched. In cases where users can skip step 1 or 2, the validation schema has errors because those fields are considered required. I have a custom validation function customValidate which returns true or false, and which is based on the current state of formikProps.errors.step3. The submit button is disabled until customValidate returns true, so I really don't need formik to validate on submit - in fact, I need it to not validate on submit, so that the onSubmit function fires properly from step 3, regardless of errors in step 1 or 2.

How can I keep validateOnMount and validateOnBlur, but prevent validate on submit? I wish there was a validateOnSubmit={false} option, but I don't see that. There must be a way?

1

1 Answers

0
votes

I have a workaround for you: Just remove the validation Schema, validateOnMount, validateOnBlur because we are going to handle them ourselves.

make your Formik as Follows

<Formik
  initialValues={initialValues}
/>

you will need this function to format the errors for you:

const getErrorMessages = ({ path, message, inner }) => {
    if (inner && inner.length) {
      return inner.reduce((acc, { path, message }) => {
        acc[path] = message;
        return acc;
      }, {});
    }
    return { [path]: message };
};

and then you can validate your form onMount using useEffect like follows

useEffect(() => {
  try {
    validationSchema.validateSync(values, { abortEarly: false });
    setErrors({});
  } catch (error) {
    setErrors({ ...getErrorMessages(error) });
  }
}, []);

and for the onBlur make your own Function

const handleBlur = async (e) => {
  setFieldTouched(e.target.name, true);
  try {
    validationSchema.validateSync(values, { abortEarly: false });
    setErrors({})
  } catch (error) {
    setErrors({ ...getErrorMessages(error) });
  }
};

finaly to your inputs you can make them as follows:

<input
  name="step1Values.someValue"
  value={step1Values.someValue}
  onChange={formikProps.handleChange}
  onBlur={handleBlur}
/>

final Notes:

  1. you have to use your custom onBlur on all fields or it won't validate onBlur
  2. you can make the same thing with onChange if you want just make a custom function like handleBlur one.