formik icon indicating copy to clipboard operation
formik copied to clipboard

Formik doesn't update isValidating onBlur and onChange, only onSubmit

Open mohsinulhaq opened this issue 6 years ago • 15 comments
trafficstars

🐛 Bug report

Current Behavior

isValidating doesn't reflect async validator being run onBlur and onChange even if it actually runs, as evident by ErrorMessage rendering

Expected behavior

isValidating should update even onBlur and onChange I checked in react-final-form, and it does the correct thing there, updating validating.

Reproducible example

https://codesandbox.io/s/hardcore-proskuriakova-qp9uc

Software Version(s)
Formik 1.5.7
React 16.8.6
TypeScript N/A
Browser Chrome 74
npm/Yarn [email protected]
Operating System Windows

mohsinulhaq avatar Jun 21 '19 12:06 mohsinulhaq

Agree isValidating should be updated always when validation is running. It seems to be how it was but it was changed not too long ago #1169

royipressburger avatar Jul 21 '19 12:07 royipressburger

I have the same issue. This becomes a bigger problem when we have async validation where it takes some time and the isValidating is always false

ematipico avatar Jul 25 '19 07:07 ematipico

@mohsinulhaq I think this is fixed in v2

ematipico avatar Jul 25 '19 07:07 ematipico

@ematipico Doesn't look like it, https://codesandbox.io/s/lucid-ride-0h9lp

mohsinulhaq avatar Jul 25 '19 07:07 mohsinulhaq

Yeah you are right. I just looked inside the code and I thought it was fixed. Never mind :)

ematipico avatar Jul 25 '19 09:07 ematipico

a solution is to duplicate the logic from field validations on L408 and integrate it into the validateFormWithLowPriority function

shrugs avatar Aug 09 '19 12:08 shrugs

I spent most of the workday trying to figure out if my formik wrapper was buggy because isValidating wasn't being set and all my tests weren't working, as I send in a spy on the context to know if isValidating is true.

Looks like I'll need to manually validate the form to set this bit, but it really seems there should be some way of knowing if validation is ongoing. The unfortunate part is now my tests will differ from a normal user flow because I'll need to manually call this method, so that's not good.

evankennedy avatar Sep 06 '19 23:09 evankennedy

We have run into the same problem. Right now, a field that requires async validation is marked as valid while waiting for the server response, after which it can jump to invalid if the validation failed. Very confusing for our users.

lingolex avatar Jan 06 '20 12:01 lingolex

Problem was/is that firing isValidating was causing too many rerenders and crushing perf

jaredpalmer avatar Jan 06 '20 18:01 jaredpalmer

@jaredpalmer so is there a workable solution to identifying if validation is happening while avoiding the rerender issue?

justinwaite avatar Jan 27 '20 19:01 justinwaite

Looks like you can observe values change to commit some action after validation:

const {
    ...
    isValid,
    values,
  } = props;
  const _values = React.useRef(values);
  // isEqual from lodash
  if (!isEqual(_values.current, isValid)) {
   // form has changed and it`s valid/invalid
    console.log(values, isValid)
  }

Mainly depends on your`s purposes. Hope, can help anybody

LFFATE avatar Jan 28 '20 06:01 LFFATE

@jaredpalmer I understand that always setting isValidating can bring the library to have performance issues (even if IMHO would be consistent and helpful), indeed that's why validateFormWithLowPriority is executed when validateOnChange or validateOnBlur is true, without dispatching SET_ISVALIDATING.

My question is: how much correct is executing validateFormWithHighPriority when using a FieldArray? Because to be consistent with the documentation, that flag should be true only on submit or if you call the form validation manually, but here in FieldArray you are doing it under the hood: https://github.com/jaredpalmer/formik/blob/master/packages/formik/src/FieldArray.tsx#L147 and you can end up to have that flag set without understanding why.

AndreaScn avatar Mar 24 '20 12:03 AndreaScn

Currently having this issue on a client project where we're using Formik - would be great if this could be looked into!

If performance is the reason this is blocked, it's probably worth mentioning that in Fielder we track async validation states at all times (not just submission) and haven't encountered any performance issues. Looking at the source code, the performance issues are because Formik isn't aggregating state updates.

Example on validation complete

Here, here and here are examples of multiple state updates occurring in succession. This leads to lots of rerenders and also awkward intermediary (aka. single render) states like this:

{ validating: true, error: undefined }    // Initial correct state
{ validating: false, error: undefined }   // Intermediary state - there is an error but it isn't shown until the next render
{ validating: false, error: someError }   // Final correct state

Example on validation started

Another example is when validation occurs. Rather than updating the value and validation state on a value change, Formik looks to be treating these as two seperate states.

{ validating: false, value: 'abc' }     // Initial correct state
{ validating: false, value: 'abcd' }   // Intermediary state - value changed and validating but isn't shown until the next render
{ validating: true, value: 'abcd' }   // Final correct state

TL;DR

You should be able to get around the performance issues by doing the following:

  • On value change, set value and validation error states in a single state change (1 render vs 2 renders)
  • On value change w/ async validation, set value and isValidating = true in a single state change (1 render vs 2 renders)
  • On async validation complete, set isValidating = false and errors in a single state change (1 render vs 2 renders)

I appreciate this is easier said than done as Formik has been around long before React hooks were introduced but hope this helps 🙏

andyrichardson avatar Jul 15 '20 12:07 andyrichardson

Is this being looked into?

My problem is I have my own hook for updating my own state when formik values change. I then query the server when my state changes. I obviously don't want to update my state if the formik values are not valid. Yet formik doesn't provide any consistent way to know whether the form is valid or not.

const previousFormikState = usePrevious({ values: { ...formik.values }, isValidating: formik.isValidating, isValid: formik.isValid });

useEffect(() => {
  if (formik.values && !deepEqual(previousFormikState, { values: { ...formik.values }, isValidating: formik.isValidating, isValid: formik.isValid })) {
            if (!formik.isValidating && formik.isValid) {  // this is the problem.  isValidating is false and isValid is true when input is invalid.
// only on subsequent render does isValid change to false.
                dispatch({
                    type: 'setFormState',
                    value: { ...formik.values }
                });
            }
        }
    }, [formik.values, formik.isValid, dispatch, previousFormikState, formik.isValidating]);

As it stands I'll have to introduce my own validated and isValid flags to my own state, then run validation again with the yup schema which is a bit ridiculous.

rossm6 avatar Oct 09 '21 15:10 rossm6

bug is still alive

Manshooo avatar Apr 23 '25 15:04 Manshooo