jaredpalmer / formik

Build forms in React, without the tears 😭
https://formik.org
Apache License 2.0
33.98k stars 2.79k forks source link

setFieldError does not prevent handleSubmit #1111

Open samjacoby opened 5 years ago

samjacoby commented 5 years ago

🐛 Bug report

This looks like it may be a possible regression on #317, as I can replicate it in the exact same way.

Current Behavior

After setting an error using setFieldError, the handleSubmit function is successfully fired.

Expected behavior

The form should not submit successfully, as there is an error.

Reproducible example

Click "Set Error". You will then be able to successfully submit. https://codesandbox.io/s/43owlv689

Suggested solution(s)

Your environment

Software Version(s)
Formik 1.3.2
React 16.4.2
TypeScript
Browser Chrome
npm/Yarn 6.1.0
Operating System OSX
samjacoby commented 5 years ago

It looks like this might also be related to https://github.com/jaredpalmer/formik/issues/706, whereby there is not a clear way to prevent automatic validation from overwriting custom errors.

jaredpalmer commented 5 years ago

This behavior is correct. Logic that sets errors imperatively will be overridden by validation. If you have an alternative API that could support this behavior lmk. The problem is you can't simply merge the old errors object with the new one, nor would you want to. As explained in my comment, you'll want to move this logic to <Field validate> if you cannot express validation it with <Formik validate> or <Formik validationSchema>. This ensures proper orchestration prior to submit.

jaredpalmer commented 5 years ago

Thinking more deeply.....

I guess I never really thought about this case. In Formik, validation functions are the way to express validation requirements and the assumption, which may be not exactly correct as you've shown, is that 100% of your logic goes there. The problem is that preventing submit like this gets wonky. Right now we set all field's to touched within the parent, but maybe, in a perfect world we would re-run all blur handlers. However, this really isn't feasible either, unless we force everyone to use <Field>. Open to suggestions and further exploration. Either way we should add something to the docs about this.

jaredpalmer commented 5 years ago

Naive solution could be to fallback to current state.errors if no validation behavior exists. I think that would solve this?

samjacoby commented 5 years ago

Yes, I puzzled over this myself. There isn't a clear way to combine the imperative error handlers with the top-level validation.

Naive solution could be to fallback to current state.errors if no validation behavior exists. I think that would solve this?

This seems like it would work reasonably well, and at least clear up some confusion.

What about a solution, though, where if there doesn't exist a validator function or validationSchema for a given function, it just doesn't overwrite what's already in there? That'd be merging the results of validation with the existing state.errors? Then the contract sort've becomes, "if you use imperative error handlers, it's also your responsibility to clear them." I don't know if that is actually a weirder behavior or if that creates any more problems, but that's how I expected it to work.

jaredpalmer commented 5 years ago

Cc @slightlytyler @crosscompile @eonwhite

what do you think?

muyiwaoyeniyi commented 5 years ago

@jaredpalmer please do you have any update or workaround for this issue?

sneu012 commented 4 years ago

I am trying to do something similar. I have an address component where I use zip code to update the city and state. It shows an error when I enter invalid zip, but I am still able to submit the form, and submitting the form removes all the existing errors.

Edit p3z45m8rpq

Any ideas on how I can properly implement this?

kanishk30 commented 3 years ago

Any workarounds? Still stuck with this.

johnrom commented 3 years ago

The way I'd envision handling Persistent Validation is outlined here: https://github.com/formium/formik/issues/1309#issuecomment-774279530

But there are very few cases in which I would recommend using this, and instead recommend reusing a function inside of <Field validate={} or <Formik validate={}. For example with the above codesandboxes:

(not tagging anyone because this issue is quite old)

OP:

withFormik implementation is not possible without a custom validator as described by Persistent Validation comment. If you can rewrite your component not to use withFormik, it can be implemented like:

const MyForm = (props) => {
  const [hasManualError, setHasManualError] = useState(false);
  return <Formik {...props} validate={() => ({ manualError: hasManualError ? "Manual Error" : ""})}>
    <button onClick={() => setHasManualError(true)} />
    />
  </Formik>

sneu012:

<Field 
  name="zip" 
  validate={(zip) => 
    !looksLikeAZip(zip) 
      ? "That doesn't look like a zip."
      : !selectReusableZipApiReturnValue(zip) // so you don't flood the server
        ? "This zip didn't return any city state info, try again."
        : "" 
  } 
/>