mjangir / formik-wizard-form

Build multi step forms using Formik with ease.
http://formikwizard.manishjangir.com
MIT License
74 stars 19 forks source link

Navigating to the previous step should trigger a validation #17

Closed theor closed 2 years ago

theor commented 2 years ago

Bug report

Current Behavior

If a form step is invalid, the next button will be disabled after clicking on the previous button ODJeBPYcTd

Expected behavior

The valid step should be seen as valid and the next button should be enabled

Reproducible example

The materialui sample: https://codesandbox.io/s/material-formik-wizard-form-h37hs?from-embed

Suggested solution(s)

force a validation when going back ?

Additional context

Your environment

Software Version(s)
Formik Wizard Form 2.0.1
Formik 2.2.9
React 17.0.2
TypeScript 4.1.2
Browser chrome 95
npm/Yarn yarn 1.22.11
Operating System win 10
carlosbensant commented 2 years ago

As a workaround, I solved it by removing the disabled={isNextDisabled} from the Next button.

Being the button disabled or not, they won't be able to go next unless the form is valid (for the current step).

carlosbensant commented 2 years ago

I should note that I ended up forking this repo and adding a new line into the handleNext function to clean up the errors object. File: useWizard.ts

skoruba commented 2 years ago

@carlosbensant - can you share your fix please in the code? thanks

mjangir commented 2 years ago

I will look into the solutions today and will release a fix.

skoruba commented 2 years ago

I did two fixes in the file useWizard.ts:

  1. clean up errors like mentioned @carlosbensant
 const handlePrev = useCallback(
    (formikBag: any) => async () => {
      let isValid = true;

      // cleanup errors
      formikBag.setErrors({});

      if (isFunction(beforePrev)) {
        try {
          await beforePrev!(formikBag.values, formikBag, currentStep);
        } catch (error) {
          isValid = false;
        }
      }

      if (isValid) {
        goToPrev();
      }
    },
    [goToPrev, currentStep, beforePrev]
  );
  1. Set touched flag after validation for all fields - in method handleNext:

import { setNestedObjectValues } from 'formik';

if (validateOnNext) {
        const errors = await formikBag.validateForm();
        isValid = Object.keys(errors).length === 0;

        // set Touched to all fields
        if (!isValid) {
          formikBag.setTouched(setNestedObjectValues(errors, true));
        }
      }

This fix is very helpful in case where you have multiple fields in the form and you do not want to display all validation errors after touching the first input. I use for validation errors combination of errors['fieldName'] && touched['fieldName'] it is much better UX. 👍

Thanks!