formik
formik copied to clipboard
Promise returned from submitForm not rejected when form is invalid
🐛 Bug report
Current Behavior
When calling submitForm on an invalid form, the returned promise is resolved.
Expected behavior
When calling submitForm on an invalid form, the returned promise should be rejected according to the documentation.
Reproducible example
https://codesandbox.io/s/formik-codesandbox-template-81kio?fontsize=14
Suggested solution(s)
Reject the promise when there are errors. It seems the related code is found here: https://github.com/jaredpalmer/formik/blob/22a17b84700af503828ca27c0d2e6a283112c4ea/src/Formik.tsx#L439-L448
Additional context
Your environment
| Software | Version(s) |
|---|---|
| Formik | 1.5.7 |
| React | 16.8.6 |
| TypeScript | n/a |
| Browser | Chrome 74 |
| npm/Yarn | 6.4.1 |
| Operating System | Mac Mojave |
Did I write that in the docs? On mobile and can’t git blame.
It seems to have been written by @FBerthelot and merged by you.
Commit: https://github.com/jaredpalmer/formik/commit/4dbb93f21ee120d2e30e3707e9c3d3380c4c4c1b PR: https://github.com/jaredpalmer/formik/pull/1423
It looks like after the PR was merged, there was discussion around this problem. FWIW, I'd rather the promise actually get rejected than just fixing the documentation to match the code.
Running into this problem myself, but instead it seems submitForm doesn't return a correct promise at all. If you look at the submitForm function, it only returns the validation promise, it doesn't return anything from executeSubmit, so the promise will always return undefined no matter what.
https://github.com/jaredpalmer/formik/blob/914ccde516e36e40bcc21996bb1b95372b95689b/src/Formik.tsx#L443-L445
To fix it, I just copied the code out into my own submitForm function and returned this.props.onSubmit and now it works just fine. executeSubmit seems useless since it just calls this.props.onSubmit but isn't used anywhere else.
Spent a few hours digging around this myself tonight. Didn't realize the docs were wrong till I pulled down the source. The current documentation for 1.5.8 shows
submitForm: () => Promise
Trigger a form submission. The promise will be rejected if form is invalid.
But like justindmyers stated above, submitForm will always resolve with undefined.
This is fixed in 2.x
Can you fix this also for 1.5.x? 2.x is still in pre-release phase.
@jaredpalmer I'm using 2.0.1-rc.13 and this appears to still be broken: https://github.com/devinsm/formik-submitform-bug
The work around for now is to wrap submitForm in custom logic:
// submitForm and validateForm are Formik's submitForm and validateForm
function fixedSubmitForm({ submitForm, validateForm }) {
return new Promise((resolve, reject) => {
submitForm()
.then(validateForm)
.then(errors => {
const isValid = Object.keys(errors).length === 0;
if (isValid) {
resolve();
} else {
reject();
}
})
.catch(e => {
console.log('error: ', e);
reject();
});
});
}
Of course this causes the form to be validated twice. In some situations this may lead to unacceptable lag after the user hits submit, but for smaller forms with synchronous validation it should work fine.
Interestingly, the typing is also inconsistent with the documentation. In 1.5.8 (latest release at time of writing), dist/types.d.ts has
export interface FormikActions<Values> {
...
submitForm(): void;
...
}
Thank you @devinsm this is a useful workaround for the time being
@josephsiefers I can verify this to be the case as well
Is this fixed? Using 2.1.1 I also ran into an issue where calling SubmitForm, the returned promise is resolved even though the form is invalid.
Is this fixed? Using 2.1.1 I also ran into an issue where calling SubmitForm, the returned promise is resolved even though the form is invalid.
Same.
Looks like 2.1.2 is not rejecting when the form is invalid.
I second @sebastianpatten. The fix doesn't work even in v2.1.2
Not working in v2.1.3 too.
Seems in 2.1.4 it is still an issue
Still got the problem too.
TWIMC fixed with #1904 then reverted with #1198 2.0.7 is the only version with expected behaviour
Would love to have this fixed. 2.1.4.
I guess this was reverted because <form onSubmit={formProps.handleSubmit} would print a console error when clients do not catch the thrown error.
We need the functionality too in order to send analytics information and other side effects on failed form validation when the user tried to submit. Currently we use this workaround: https://github.com/jaredpalmer/formik/pull/2103#issuecomment-574565897, but it is not very nice, and also it might break when React Concurrent Mode lands, and Automatic batching of multiple setStates happens (which even happens in Blocking Mode).
I would suggest to add an additional config parameter to FormikConfig: onSubmitCancelledByFailingValidation.
const formProps = useFormik({
initialValues: myInitialValues,
validate: myValidateFunction,
onSubmit: mySendForm, // is only executed when validation succeeds
onSubmitCancelledByFailingValidation: myFallbackOnFailedValidation,
})
This would be backwards-compatible, and also it would separate the general validation (that can happen when e.g. just typing in an input, or blurring a field) from validation that is triggered by trying to send the form.
An alternate solution idea would be to add a second parameter to the validate config parameter to signal that the validation is being executed because the user is trying to send the form: validate?: (values: Values, validationTrigger: 'field-changed' | 'field-blurred' | 'submit') => void | object | Promise<FormikErrors<Values>>
const formProps = useFormik({
initialValues: myInitialValues,
validate: (values, validationTrigger) => {
const result = myValidateFunction()
if (validationTrigger === 'submit') {
myFallbackOnFailedValidation()
}
return result
},
onSubmit: mySendForm, // is only executed when validation succeeds
})
This would also be backwards-compatible, but not as elegant on the client side, and also would not work for a Yup validationSchema if used. Therefore I would prefer the first approach.
@jaredpalmer What do you think about these suggestions? Would you accept a PR?
This is still an issue, almost been a year. Is there any traction on resolving this?
I created a PR with my solution suggestion, constructive discussion about the approach welcome: #2485.
Is there any update with this? I've had to revert back to 2.0.7 in order to get access to the data returned from submitForm Promise.
Doesn't seem to be rejecting when the form is invalid in 2.1.5 either 😞 .
I hate to do it, but.... bump!
2.2.5 bug is still there (
Probably it is off topic, I describe my case
export const InfoForm = () => {
.................
.................
const formik = useMemo(() => ({
initialValues,
onSubmit: async (values:, actions) => {
.....
},
validate: (values: typeof initialValues) => {
const errors = {};
if (!values.reason) {
errors.reason = ....;
}
return errors;
},
}), []);
return (
<>
<Formik {...formik}>
<Form>
......
......
<Submit className={s.submit}>Submit</Submit>
</Form>
</Formik>
</>
);
};
If any error happens in onSubmit or in validate function then I just see warning An unhandled error was caught from submitForm() So I can't use Sentry here :(.
What helped me:
I extracted submit button and start use submitForm on click:
export const Submit = ({
children,
...restProps
}) => {
const { isSubmitting, submitForm } = useFormikContext();
return (
<ButtonPrimary
type='submit'
loading={isSubmitting}
{...restProps}
onClick={submitForm}
>
{children}
</ButtonPrimary>
);
};
So now I can use Sentry without any additional magic, though it is also magic :)
Any news?
// submitForm and validateForm are Formik's submitForm and validateForm function fixedSubmitForm({ submitForm, validateForm }) { ...
was till now the only way to submit a form from an button, but it also started to ignore validation ;(
I used this as a workaround:
<Button onClick={() => {
formik
.submitForm()
.then(() => {
if (formik.isValid) {
handleFormSuccessfullySent();
} else {
handleFailedValidationOnFormSubmission();
}
})
}} />
I also had validateOnMount set to true, otherwise formik.isValid condition was true when the form wasn't touched before trying to submit it:
const formik = useFormik({
// ...
validateOnMount: true
});
If you just need to reject the promise returned by the submitForm function when the form is invalid, this is an easy workaround:
const submit = () => helpers.submitForm().then(() => helpers.isValid || Promise.reject())
<SubmitButton onClick={submit}>Submit</SubmitButton>
I also needed to use @maciejtoporowicz's suggestion to add thevalidateOnMount flag.