Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

validateOnMount: Initial value for is valid should be whether the initial values are valid, when validateOnMount is true #2172

Open
andycarrell opened this issue Jan 7, 2020 · 17 comments

Comments

@andycarrell
Copy link

馃悰 Bug report

Current Behavior

When validateOnMount is true, initial value for isValid is 'true', then validation occurs on initial values (which could set isValid to 'false').

Screenshot taken from reproducible example - see console for inconsistency - isValid has a different value, for the same form value (empty string in the example):

image

Expected behavior

The first render should return the result of validating the initial values for isValid.

Reproducible example

https://codesandbox.io/s/formik-codesandbox-template-kegrs

Additional context

Would this problem be solved by setting initial errors? I believe possibly, although that would require running validation manually, defeating the purpose of validateOnMount.

The example is a simplification - application code has a far more complex validation schema + more initial values.

Your environment

Software Version(s)
Formik 2.1.1
React 16.12.0
TypeScript -
Browser Codesandbox? && Chrome
npm/Yarn 6.13.4
Operating System MacOSX 10.15.2
@omkar-joshi
Copy link

I'd like to help with this

@jaredpalmer
Copy link
Owner

Go for it!

@ks-amit
Copy link

ks-amit commented Jan 18, 2020

I'd like to help with this

Any progress on this?

@omkar-joshi
Copy link

Yeah. Putting up a PR in a couple of days

@koredefashokun
Copy link

@omkar-joshi is there any progress on this? If you're still working on the PR, I would really appreciate if you could explain any workarounds for this issue at this time. Also, is there any way to prevent Formik from setting errors after running validateOnMount. I would like to set the isValid value, but not have errors on the fields. Thanks for your anticipated help.

@btiwaree
Copy link

Yeah. Putting up a PR in a couple of days

Hi @omkar-joshi any update on this? appreciate your contribution towards OSS.

@semopz
Copy link

semopz commented Feb 26, 2020

Is it just a case of using validateFormWithHighPriority instead of validateFormWithLowPriority?

React.useEffect(() => {
if (validateOnMount && isMounted.current === true) {
validateFormWithLowPriority(initialValues.current);
}
}, [validateOnMount, validateFormWithLowPriority]);

@andycarrell
Copy link
Author

馃憢 Hi @semopz I've been looking into this a bit this weekend, and I don't think high/low priority will fix this issue unfortunately. Both validate form functions (withLow... / withHigh...) call runAllValidations which runs asynchronously - so from mount until that promise resolves, isValid will be true. This can cause an unideal user experience, specifically if isValid is used to drive form appearance, for example disabling the submit button. As a consumer of formik, I need to know what to render while validation on mount is running 馃

Some possible solutions (in user-land or in the library) I can think of are:

  • Resolve validation to initialErrors, then render
  • Bring back (from deprecation) isInitialValid - to set isValid whilst validation on mount is running
  • Somehow run validation synchronously 馃槙
  • Set isValidating to true initially, then use this value to determine what to render. (I've coded this here to show how)

Not really sure how to proceed with this, I understand to an extent why isInitialValid was deprecated, but also think removing it isn't serving us well in this scenario.

Validate form with low priority:

const validateFormWithLowPriority = useEventCallback(
(values: Values = state.values) => {
return unstable_runWithPriority(LowPriority, () => {
return runAllValidations(values)
.then(combinedErrors => {

Login button flash (enabled => disabled)

Kapture 2020-03-01 at 21 31 16

@bduffany
Copy link

bduffany commented Apr 15, 2020

Here are a few options in my view:

Option 1:

  • If validation can be run synchronously, then Formik should synchronously validate before the first render. This at least fixes the flashing issue for forms that validate synchronously (pure speculation here, but I'd guess the vast majority of forms probably have synchronous validation, so this should fix the bug for "the 80%").
  • If validation cannot be run synchronously, then force the state to be invalid until the first validation is complete. However, this does not fix the "flashing" issue because it may be the case that the form is computed to be valid after all, and then it flashes from invalid -> valid.

Option 2:

  • Un-deprecate isInitialValid and only issue a warning if the user uses more than one of { isInitialValid, validateOnMount, and initialErrors}, which could result in conflicting validation states & thus render flashing. IMO this is the simplest and best option. But it does not account for cases where we legitimately need to do an async call in order to compute whether the initial state is valid. (But really, do these cases even exist in the wild? I can't think of a real-world example where an async-validated form doesn't know its initial validation state on initial render. In fact it seems like in "80%" of cases, the initial state is invalid, so it seems like isInitialValid should have been defaulting to false this whole time!)

If people think that Option 2 makes sense, I can send a PR.

@rphlmr
Copy link

rphlmr commented Apr 16, 2020

I stick on option 2 and use isInitialValid. It fits my needs, it's a simple props to disable a submit button before user completes fields 馃槆

@crhayes
Copy link

crhayes commented May 7, 2020

Ultimately the state of isValid cannot be known until validation completes, unless it is explicitly provided (which defeats the purpose of validateOnMount).

Where I have experienced this issue, and likely where many others have, is in trying to control enabled/disabled state of a submit button.

Typically the button would be disabled while validations are running. The logic would therefore be disabled={isValidating || !isValid}.

The problem is that isValidating is false on initial render even when validateOnMount is set to true. This seems counterintuitive. Having isValidating default to true when validateOnMount is true would likely allow most of us to work around this issue.

@jktravis
Copy link

jktravis commented Nov 5, 2020

Status?

@vai0
Copy link

vai0 commented Jan 14, 2021

To add to this discussion, it seems the problem is because computing isValid dependent on dirty, but dirty is already set to true before the form finishes its first validation - this.state.errors is still empty at this point, computing isValid to true when it shouldn't be.
https://github.com/formium/formik/blob/master/packages/formik/src/Formik.tsx#L940-L950

one possible solution is:

  1. add an additional validated flag (as a global, or part of the state, not sure) that gets set to true when runAllValidations resolves
  2. have the isValid computation depend on flag validated
    const isValid = dirty && validated ? ...
    

if this makes sense, i can submit a PR

@no-creative-name
Copy link

@vai0 I like your approach. Are you still open for submitting this PR?

@no-creative-name
Copy link

@vai0 push 馃

@ctharings
Copy link

Jan 6, 2020 馃槥

@mitidiero
Copy link

Any solutions or workarounds?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests