Open Aaronius opened 5 years ago
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.
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.
I don‘t understand why validateOnMount
is necessary, submitForm
should trigger validation in any case, what am I missing?
@callumjg Doesn't you use stale helpers object from previous render in your then handler?
@callumjg Doesn't you use stale helpers object from previous render in your then handler?
I haven't come across that problem. I'm pretty sure that the submit function gets reevaluated every render with the lastest helpers object (at least in my implementation). Only the initial render seemed to be a problem, but this was solved by using validateOnMount.
@callumjg what khanilov means is that if there is an update to isValid between the time this last render was committed and then()
, it will not be reflected in helpers.isValid
as the helpers object is not modified in the submit function.
Generally isValid isn't changed during this time if you happen to be using synchronous validation and validating onBlur or onChange, because validation would have already occurred at that time for a given set of values, which could explain why you may not be experiencing this issue.
In v3 you will be able to use formik.getState().isValid
to get the latest value.
... Generally isValid isn't changed during this time if you happen to be using synchronous validation and validating onBlur or onChange, because validation would have already occurred at that time for a given set of values, which could explain why you may not be experiencing this issue. ...
Thanks for clarifying. Yes, that describes my use case.
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 the
validateOnMount
flag.
This just basically solved the problem, thank you so much 🥂
Yo, today's Sunday, August the 20th, year 2023, exactly 4 years, 2 months and 17 days since this issue report was created.
How is it possible that this is unresolved to this day? What's the point of using Formik if it can't handle validation for you?
Still waiting for fixes
Found temporal solution in doing validateForm() again and check for errors:
formik.submitForm().then(() => {
return formik.validateForm()
.then((errors) => {
return _.isEmpty(errors) ? Promise.resolve() : Promise.reject();
});
}).then(() => {
// actions on success
}).catch(() => {
// actions on error
});
It is still not fixed for 2.4.5
🐛 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