Open jordi-farre opened 5 years ago
You can create Effect component which tracks form submit count and trigger callback when needed. See my implementation of very custom effect for this particular use case. https://codesandbox.io/s/7ky70mnv6j
function App() {
return (
<Formik
initialValues={{ email: "" }}
onSubmit={() => console.log("sudmitted without errors")}
validate={values => {
const errors = {};
if (!values.email) {
errors.email = "Email is missing";
}
return errors;
}}
>
{formik => (
<Form onSubmit={e => e.preventDefault()}>
<Effect
formik={formik}
onSubmissionError={() => console.log("sudmitted with error")}
/>
<Field name="email">
{fieldPros => <input placeholder="Email" {...fieldPros.field} />}
</Field>
<button type="submit" onClick={formik.submitForm}>
Submit
</button>
</Form>
)}
</Formik>
);
}
function Effect(props) {
const effect = () => {
if (props.formik.submitCount > 0 && !props.formik.isValid) {
props.onSubmissionError();
}
};
React.useEffect(effect, [props.formik.submitCount]);
return null;
}
Note: this can be implemented without hooks with a little more work (tracking props in componentDidUpdate lifecycle method)
@Andreyco Thanks for code snippet. Just wondering if you have figured out how to pass list of errors to onSubmissionError()
via props.formik
. I have tried to log out formik's errors in effect()
, it returns empty object for the first time, then submit again then errors
will return. Thanks!
function Effect(props) {
const effect = () => {
if (props.formik.submitCount > 0 && !props.formik.isValid) {
console.log('log errors', props.formik.errors); // return empty obj
props.onSubmissionError(props.formik.errors);
}
};
React.useEffect(effect, [props.formik.submitCount]);
return null;
}
I think this should be a function of this library, I hope @jordi-farre's PR will be accepted. Thank you for the workaround @Andreyco! @pandaduc, to have the error messages populated you can change the function as follows:
function Effect(props) {
const effect = () => {
if (props.formik.submitCount > 0 && !props.formik.isSubmitting && !props.formik.isValid) {
props.onSubmissionError();
}
};
React.useEffect(effect, [props.formik.submitCount, props.formik.isSubmitting]);
return null;
}
BTW, I extracted @Andreyco's workaround into a separate component using formik's connect()
method:
import React from 'react';
import { connect } from 'formik';
function OnSubmitValidationError(props) {
const { callback, formik } = props;
const effect = () => {
if (formik.submitCount > 0 && !formik.isSubmitting && !formik.isValid) {
callback(formik);
}
};
React.useEffect(effect, [formik.submitCount, formik.isSubmitting]);
return null;
}
export default connect(OnSubmitValidationError);
The component can be used like this:
<Formik {...formikProps}>
<OnSubmitValidationError
callback={this.onSubmitValidationError} />
</Formik>
Note that this solution relies on the <OnSubmitValidationError>
component being re-rendered when the <Formik>
component updates.
@skleeschulte I would say otherwise... the less surface are Formik has, the better. It still allows to create escape hatches for multiple scenarios.
I like this flexibility a lot.
Thanks for the workaround @skleeschulte 👏
Just to add my use case to this as I think it would be a great feature to officially support:
We wanted to send an analytics event with information about validation errors, but we didn't want to send it every single time validation ran, only when the user pressed the 'submit' button. Without these workarounds, this isn't possible as the error object is empty when you initially submit.
I'd like to make sure my explanation of this approach is clear in the linked PR above- if anyone has comments please chime in and thank you! https://github.com/artsy/reaction/pull/2727/files#diff-f2a7f37a8bd6fff8c24a1b0f3862fa1fR197-R208
Thanks to Stefan for the solution above, sharing our slight modification in case it helps anyone else who ends up here:
const SubmitErrorHandler = ({ submitCount, isValid, onSubmitError }) => {
const [lastHandled, setLastHandled] = useState(0);
useEffect(() => {
if (submitCount > lastHandled && !isValid) {
onSubmitError();
setLastHandled(submitCount);
}
}, [submitCount, isValid, onSubmitError, lastHandled]);
return null;
};
Adding the state variable is needed if you want to avoid handling a failed submission multiple times.
Thanks to everyone for ideas and code, sharing my slight improve of the last provided code. It's possible to don't pass all required formik variables but instead get them directly in the component using useFormikContext:
function FormikSubmitEffect({ onSubmitError }) {
const { submitCount, isValid } = useFormikContext()
const [lastHandled, setLastHandled] = useState(0)
useEffect(() => {
if (submitCount > lastHandled && !isValid) {
onSubmitError()
setLastHandled(submitCount)
}
}, [submitCount, isValid, onSubmitError, lastHandled])
return null
}
@iamness @DimaDK02 I believe you need to use && !isSubmitting
like this:
if (submitCount > lastHandled && !isSubmitting && !isValid) {
onSubmitError();
setLastHandled(submitCount);
}
I also need such callback. Was it added? When the user submit a form and there are validation errors, we usually show a notification telling the user that there are errors in the form.
🚀 Feature request
Current Behavior
Currently when you submit the form and there are validation errors in the form, the onSubmit method is not called, but there is no method to be called when this happens.
Desired Behavior
Pass a function as a formik parameter, named onSubmitValidationError that will be called when you trigger the submit action but there are validation errors in the form. This could be useful to perform some extra action outside of the form.
Suggested Solution
I have created a PR with a possible solution:
https://github.com/jaredpalmer/formik/pull/1471
Who does this impact? Who is this for?
All users who want to perform extra actions outside of the form, when this happens. I thinks it's something more useful for advanced users.
Describe alternatives you've considered
I think you can play with other properties like errors, submitting, etc. But I found it a little more complicated than having a callback.
Additional context
https://github.com/jaredpalmer/formik/pull/1471