formik
formik copied to clipboard
Formik doesn't update isValidating onBlur and onChange, only onSubmit
🐛 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 |
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
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
@mohsinulhaq I think this is fixed in v2
@ematipico Doesn't look like it, https://codesandbox.io/s/lucid-ride-0h9lp
Yeah you are right. I just looked inside the code and I thought it was fixed. Never mind :)
a solution is to duplicate the logic from field validations on L408 and integrate it into the validateFormWithLowPriority function
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.
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.
Problem was/is that firing isValidating was causing too many rerenders and crushing perf
@jaredpalmer so is there a workable solution to identifying if validation is happening while avoiding the rerender issue?
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
@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.
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 🙏
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.
bug is still alive