How to properly use Formik's setError method? (React library)
Formik author here...
setError
was deprecated in v0.8.0 and renamed to setStatus
. You can use setErrors(errors)
or setStatus(whateverYouWant)
in your handleSubmit
function to get the behavior you want here like so:
handleSubmit = async (values, { setErrors, resetForm }) => {
try {
// attempt API call
} catch(e) {
setErrors(transformMyApiErrors(e))
// or setStatus(transformMyApiErrors(e))
}
}
What's the difference use setStatus
vs. setErrors
?
If you use setErrors
, your errors will be wiped out by Formik's next validate
or validationSchema
call which can be triggered by the user typing (a change event) or blurring an input (a blur event). Note: this assumed you have not manually set validateOnChange
and validateOnBlur
props to false
(they are true
by default).
IMHO setStatus
is actually ideal here because it will place the error message(s) on a separate part of Formik state. You can then decide how / when you show this message to the end user like so.
// status can be whatever you want
{!!status && <FormError>{status}</FormError>}
// or mix it up, maybe transform status to mimic errors shape and then ...
{touched.email && (!!errors.email && <FormError>{errors.email}</FormError>) || (!!status && <FormError>{status.email}</FormError>) }
Be aware that the presence or value of status
has no impact in preventing the next form submission. Formik only aborts the submission process if validation fails.
Another way to handle this situation is to assign a specific key to your api
errors and use setStatus
for status messages.
__handleSubmit = (values, {setStatus, setErrors}) => {
return this.props.onSubmit(values)
.then(() => {
setStatus("User was updated successfully.");
})
.catch((err) => {
setErrors({api: _.get(err, ["message"])});
});
}
Then any validation errors would appear by the fields and any api errors could appear at the bottom:
<Formik
validationSchema={LoginSchema}
initialValues={{login: ""}}
onSubmit={this.__handleSubmit}
>
{({isSubmitting, status, errors, values, setFieldValue}) => (
<Form className={classNames("form")}>
<FormGroup>
<InputGroup>
<InputGroup.Text>
<FontAwesomeIcon icon={faUser} fixedWidth />
</InputGroup.Text>
<Field
name="login"
type={"text"}
placeholder="Login"
className="form-control"
/>
</InputGroup>
<ErrorMessage name="login" />
</FormGroup>
<Button type="submit" variant="primary" disabled={isSubmitting}>
Submit
</Button>
{errors && _.has(errors, ["api"]) && <div className="text-danger">{_.get(errors, ["api"])}</div>}
{status && <div className="text-success">{status}</div>}
</Form>
)}
</Formik>
Don't forget about the schema...
const LoginSchema = Yup.object().shape({
login: Yup.string()
.min(4, 'Too Short!')
.max(70, 'Too Long!')
.required('Login is required'),
});
The api
error message will show until the next validation call by Formik (i.e. the user is fixing something). But the status
message will stay until you clear it (with a timer or Fade).