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 statusThis happens because validation occurs before submit, hence setStatus('')
is not called.
What's the recommended way to deal with this?
it is very simple just do console. log(formik. values) and you will get all the values without submitting it.
Why does Formik touch all fields before submit? It is common practice to only show an input's errors in the UI if it has been visited (a.k.a "touched"). Before submitting a form, Formik touches all fields so that all errors that may have been hidden will now be visible.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With