How do I split Yup validation in a two-step Formik form (React)?

Viewed 438

I'm trying to build a multi-step form with the help of Formik. The first step includes required fields, while the summary step has an optional subscribe checkbox with an email field popping up on demand (required if the checkbox is checked). The problem I'm having right now is that when I'm getting to a checkbox after successful validation of the first step, the email field is already showing a Yup error as if it's been already validated before.

I tried to fix this by dividing the validation schema into an array depending on a currentStep so that every step has its own pack of conditions, but the problem remains, even though the email field hasn't been mentioned during the first step validation. Please see my code:

export const FormikStepper = ({ children, ...props }) => {
  const childrenArray = Children.toArray(children);
  const [step, setStep] = useState(1);
  const currentChild = childrenArray[step];
  const isLastStep = () => step === childrenArray.length - 1;

  return (
    <Formik
      {...props}
      validationSchema={validationSchema[step - 1]}
      onSubmit={async (values, helpers) => {
        if (isLastStep()) {
          await props.onSubmit(values, helpers);
        } else {
          setStep((s) => s + 1);
        }
      }}
    >
      {(props) => (
        <Form id="msform" autoComplete="off">
          {childrenArray[0]}

          <fieldset>
            {isLastStep() ? (
              <SummaryStep values={props.values} />
            ) : (
              currentChild
            )}

            <div className="buttons-container">
              {step > 1 ? (
                <button
                  type="button"
                  onClick={() => setStep((s) => s - 1)}
                  className="action-button"
                >
                  Back
                </button>
              ) : null}
              <button className="action-button" type="submit">
                {isLastStep() ? "Submit" : "Next"}
              </button>
            </div>
          </fieldset>
        </Form>
      )}
    </Formik>
  );
};

My Yup validation schema:

  const validationSchema = [
  Yup.object({
    firstName: Yup.string()
      .max(15, "Must be 15 characters or less")
      .required("Required"),
    lastName: Yup.string()
      .max(15, "Must be 20 characters or less")
      .required("Required"),
    phoneNumber: Yup.string()
      .matches(phoneRegExp, "Phone number is invalid")
      .required("Required"),
    country: Yup.string()
      .notOneOf(["selectYours"], "Please choose your country")
      .required("Required"),
  }),
  Yup.object({
    subscribe: Yup.boolean(),
    email: Yup.mixed().when("subscribe", {
      is: true,
      then: Yup.string()
        .email("Invalid email address")
        .required("We need your email to keep you up to date"),
      otherwise: Yup.string(),
    }),
  }),
];

P.S. My code snippet may look dirty, that's my personal pain. I didn't manage to pass values props to a currentChild, so I had to make a conditional statement, hardcode <SummaryStep /> component, and pass them directly.

0 Answers
Related