Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Formik - Validating Form on Component Re-Mounting and Enabling Button

I'm writing a React app and using Formik and Yup for forms. The page I'm having trouble with is a wizard-style page that renders Form1 on page load with a "Next" button on it. Pressing "Next" renders Form2 and a "Back" and "Next" button appear with Form2, which move you through the wizard.

The trouble is using the Back button. After I finish filling in Form1 the "Next" button becomes enabled and I can move to the next form. When I'm on Form2, I want to be able to go back to Form1 and press the "Back" button. It loads the values correctly into the form when the Formik is re-rendered, but it doesn't validate the form and therefore the "Next" button is disabled, even though the data is valid. I would have to cause a change event on one of the Fields to get the "Next" button to be enabled.

In this situation, the "Next" button should be enabled when the form re-renders. I've looked through the documentation and tried the validateOnMount prop in Formik, which by it's name should work. The docs recommend using initialErrors, but I'm not clear on how to use that. The problem may also be how I'm enabling/disabling the "Next" button.

This has been driving me crazy for hours and is the last thing to finish up this page on a Friday afternoon.

<Formik
     enableReinitialize
     validationSchema={validationSchema}
     initialValues={initialValues}
     validateOnMount
     render={({ isValid, values, isSubmitting, handleChange, setFieldValue, setFieldTouched }) => (
         <Form className="text-center">
            <FieldRow labelName="What's your address? (Can't be a PO Box)">
                <AddressField
                   id="address"
                   name="address"
                   data-testid="address"
                   onChange={handleChange}
                   onBlur={setFieldTouched}
                   values={values}
                   setFieldValue={setFieldValue}
                   validateForm={validateForm}
                /> </FieldRow>
               <div className="mt-5">
                  <Button
                    data-testid="submit-next"
                    color="primary"
                    onClick={() => handleNext(values)}
                    className="float-right"
                    disabled={isValid === false || isSubmitting}
                 >
                    {isSubmitting ? <Spinner /> : 'Next'}
                 </Button>
                 <Button
                    data-testid="submit-back"
                    color="light"
                    onClick={() => handleBack()}
                    className="float-left"
                    disabled={true}
                  >
                    Back
                  </Button>
              </div>
          </Form>
      )}
/>
like image 656
bluedevil2k Avatar asked May 08 '20 18:05

bluedevil2k


1 Answers

The reason why that's not working is because the first time you go to that page the Formik component is mounted and rendered. Then, when you press next or back, it's not mounted again, it's just updated, so it will not trigger the validation again.

If you have back and next buttons I assume you use them to increment an index or step variable/state. What you could do is manually trigger the validation calling validateForm() from an useEffect that has the step state as deps:

<Formik
  enableReinitialize
  validationSchema={ validationSchema }
  initialValues={ initialValues }
  validateOnMount
  render={({
    isValid,
    values,
    isSubmitting,
    handleChange,
    setFieldValue,
    setFieldTouched,
    validateForm,
  }) => {
    useEffect(() => {
      // This will be called everytime `step` changes:
      validateForm();
    }, [step]);

    return (<Form ...>...</Form>)
  }}
/>

Alternatively, you could try using initialErrors, but this might trigger the validation when there's actually no need to do so if there's something else apart from step that might cause a re-render:

<Formik
  initialErrors={ validationSchema.isValidSync(initialData) }
  ...
/>

Also, it looks like there's a related bug currently open and the suggested temporary fix is using useEffect as well, so even if you go for initialErrors, you might still need to add useEffect until this issue is fixed:

https://github.com/jaredpalmer/formik/issues/1950

like image 144
Danziger Avatar answered Nov 04 '22 17:11

Danziger