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>
)}
/>
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With