I'm using Formik with yup validation, following this pattern:
const handleSubmit = async (values, { setStatus }) => {
setStatus(''); // clean errors messages
try {
... do submit work ...
const res = await sendData( convertValues(values)); //
... more work
} catch (e) {
console.error(e);
setStatus('an error occurred '...);
}
};
const validationSchema = Yup.object({
code: Yup.string().required(t('Required')),
....
});
return (
<Formik onSubmit={handleSubmit} initialValues={initialValues} validationSchema={validationSchema}>
{({ setFieldValue, status }) => (
<Form>
....
<MyErrorComponent msg={status} />
</Form>
)}
</Formik>
);
This has problems in this scenario:
handleSubmit
. An error message is shown in status This happens because validation occurs before submit, hence setStatus('')
is not called. What's the recommended way to deal with this?
I think u should use something like this:
<Formik
initialValues={
firstName: '',
email: ''
}
validationSchema={
Yup.object().shape({
email: Yup.string()
.email("Must be a valid email")
.max(255)
.required("Email is required"),
firstname: Yup.string()
.max(255)
.required("First name is required"),
})
}
onSubmit={(e, { resetForm }) => {
let val = e;
postSubmit (e.firstName,e.email);
// ur rest logic
// resetForm({
// values: {
// ...e,
// },
});
}}
>
{({ handleChange, values, initialValues, errors }) => (
// Your form jsx
)}
</Formik>
Don't use useState
with formik, let Formik take care of error and validation!
I did use this pattern and it works perfectly.
If you didn't find the way, share your form ill make it for you!
It has a validation process on onMouseDown event. You can avoid duplicate validation by prevent default on Submit button.
onMouseDown={(event: any): void => {
event.preventDefault();
}}
after that Formik automatically clear status
you can use hooks for formik useFormik
import { useFormik } from 'formik';
....
const formik = useFormik({
initialValues: {},
validationSchema={validationSchema}
onSubmit: values => {
handleSubmit(values);
},
});
const handleSubmit = async (values, { setStatus }) => {
try {
... do submit work ...
const res = await sendData( convertValues(values)); //
... more work
} catch (e) {
console.error(e);
//change statuvalues or display errors
//formik.setValue...
//formik.setError...
}
};
return (
<form onSubmit={formik.handleSubmit}>
....
<button type="submit">Submit</button>
</form>
);
You can control when Formik runs validation by changing the values of
<Formik validateOnChange> and / or
<Formik validateOnBlur> props depending on your needs.
By default, Formik will run validation methods as follows:
Pass to your Formik the props
validateOnChange={false} and
validateOnBlur={false}
For better understanding check the docs Formik Validation
Personally I think that status is only to notify user. So I would notify them from a snackbar / notification by telling them there is an error. Then I would remove them if there's an error on the validation form so they would focus on fixing the wrong one
Here's an example code and sandbox :
const validationSchema = yup.object().shape({
name: yup.string().required("Required")
});
const initialValues = {
name: "John Doe"
};
export const FormikTextField = ({ className, ...props }) => {
const [field, meta] = useField(props);
const { setStatus } = useFormikContext();
useEffect(() => {
if (meta.error) {
setStatus("");
}
}, [meta.error]);
return (
<>
<TextField
variant="outlined"
{...field}
{...props}
FormHelperTextProps={{ error: true }}
helperText={meta.error && meta.touched ? String(meta.error) : null}
aria-invalid={Boolean(meta.error)}
/>
</>
);
};
export default function App() {
const handleSubmit = (values, { setStatus }) => {
setStatus("status set!");
};
return (
<Formik
enableReinitialize
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{(props) => (
<Form>
{props.status && <p>{props.status}</p>}
<div className="App">
<FormikTextField name="name" variant="outlined" label="Name" />
<Button type="submit" variant="contained">
Submit
</Button>
</div>
</Form>
)}
</Formik>
);
}
Yes, your assumption is correct. Form validation done before submission. What you have to do is, add this errors clearing step in a custom validate function as
const validate = values =>
{
// If you want some other custom validations, you can do that on values parameter
setStatus('');
};
const handleSubmit = async (values, { setStatus }) => {
// setStatus(''); // clean errors messages
try {
... do submit work ...
const res = await sendData( convertValues(values)); //
... more work
} catch (e) {
console.error(e);
setStatus('an error occurred '...);
}
};
const validationSchema = Yup.object({
code: Yup.string().required(t('Required')),
....
});
return (
<Formik onSubmit={handleSubmit} initialValues={initialValues} validationSchema={validationSchema}
validate={validate}>
{({ setFieldValue, status }) => (
<Form>
....
<MyErrorComponent msg={status} />
</Form>
)}
</Formik>
);
For more details, check this CombinedValidations example.
As you are using Async form submission as illustrated in async-submission . You might have to either use async validation function by creating a wrapper Promise like this or make you form submission sync.
Above examples' links are from official docs .
You can add validate
call in every form input so when ever user change input status
message will be cleared.
import React from "react";
import ReactDOM from "react-dom";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
const schema = Yup.object().shape({
name: Yup.string().required("Name"),
age: Yup.number().required("Number").positive().integer()
});
const MyForm = () => (
<div>
<Formik
initialValues={{ name: "", age: "" }}
validationSchema={schema}
onSubmit={(values, actions) => {
console.log("submited", values);
actions.setStatus("")
try {
throw new Error("Something went wrong")
}
catch (error) {
actions.setStatus(error.message);
}
}}
render={({
status,
isSubmitting,
setStatus,
dirty,
values,
handleReset,
errors,
touched
}) => {
const field_props_with_validation = function() {
const name = arguments[0];
const type = arguments[1] || "text";
const placeholder = arguments[2] || "";
return {
name,
type,
placeholder,
validate: () => {
setStatus("");
}
}
}
return (
<Form>
{status}
{["name", "age"].map((field, key) => (
<div key={key}>
<Field {...field_props_with_validation(field)} />
<ErrorMessage name={field} component="div" />
</div>
))}
<button type="submit">Enter</button>
</Form>
);
}}
/>
</div>
);
function App() {
return (
<div className="App">
<h1>Registration Form</h1>
<MyForm />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
validation
can be disabled
on form input change using validateOnChange
props and only validate
when user submits the form.
Note: In this case you would have to handle the validation, status will not be cleared until user resubmits the form.
import React from "react";
import ReactDOM from "react-dom";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
const schema = Yup.object().shape({
name: Yup.string().required("Name"),
age: Yup.number().required("Number").positive().integer()
});
const MyForm = () => (
<div>
<Formik
initialValues={{ name: "", age: "" }}
validateOnChange={false}
onSubmit={async (values, actions) => {
actions.setStatus("");
await schema.validate(values, {
abortEarly: false // not to stop on single validation fail
// and return single error message
})
.then(() => {
try {
throw new Error("Some error")
}
catch (error) {
actions.setStatus(error.message);
}
})
.catch(err => {
let errors = {};
Object.keys(values).forEach((key, idx) => {
if (err && err.errors && err.errors[idx]) {
errors[key] = err.errors[idx];
}
});
actions.setErrors(errors)
})
}}
render={({
status,
isSubmitting,
dirty,
values,
handleReset,
errors,
touched
}) => {
return (
<Form>
{status}
{["name", "age"].map((field, key) => (
<div key={key}>
<Field name={field} type="text" placeholder={field} />
<ErrorMessage name={field} component="div" />
</div>
))}
<button type="submit">Enter</button>
</Form>
);
}}
/>
</div>
);
function App() {
return (
<div className="App">
<h1>Registration Form</h1>
<MyForm />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.