J'utilise React pour communiquer avec un serveur. Essaye maintenant d'implémenter correctement Formik (bibliothèque de formulaires).
Question principale: Comment utiliser correctement la méthode setError de Formik?
Les erreurs de validation côté client s’affichent correctement, mais j’essaie maintenant de définir/afficher les erreurs de validation d’arrière-plan, qui sont renvoyées avec une réponse avec le code d’état 400.
Lien vers la documentation sur la méthode que j'essaie d'utiliser
J'utilise cette méthode dans la méthode nommée handle400Error dans le code ci-dessous.
Mon code React (et Formik)]:
import React, { Component } from "react";
import axios from "axios";
import { Formik } from "formik";
import * as Yup from "yup";
import styled from "styled-components";
import FormError from "../formError";
const Label = styled.label``;
class LoginForm extends Component {
initialValues = {
password: "",
username: ""
};
getErrorsFromValidationError = validationError => {
const FIRST_ERROR = 0;
return validationError.inner.reduce((errors, error) => {
return {
...errors,
[error.path]: error.errors[FIRST_ERROR]
};
}, {});
};
getValidationSchema = values => {
return Yup.object().shape({
password: Yup.string()
.min(6, "Password must be at least 6 characters long")
.required("Password is required!"),
username: Yup.string()
.min(5, "Username must be at least 5 characters long")
.max(40, "Username can not be longer than 40 characters")
.required("Username is required")
});
};
handleSubmit = async (values, { setErrors }) => {
console.log("handleSubmit");
try {
const response = await axios.post(
"http://127.0.0.1:8000/rest-auth/login/",
values
);
const loginToken = response.data["key"];
this.handleLoginSuccess(loginToken);
} catch (exception) {
// Expected: 400 status code
if (exception.response && exception.response.status === 400) {
// Display server validation errors
this.handle400Error(exception.response.data, setErrors);
}
console.log("exception", exception);
console.log("exception.response", exception.response);
}
};
handle400Error = (backendErrors, setErrors) => {
let errors = {};
for (let key in backendErrors) {
errors[key] = backendErrors[key][0]; // for now only take the first error of the array
}
console.log("errors object", errors);
setErrors({ errors });
};
handleUnexpectedError = () => {};
handleLoginSuccess = loginToken => {
console.log("handleLoginSuccess");
this.props.setGreeneryAppState({
loginToken: loginToken
});
this.props.history.replace(`/${this.props.locale}/`);
};
validate = values => {
const validationSchema = this.getValidationSchema(values);
try {
validationSchema.validateSync(values, { abortEarly: false });
return {};
} catch (error) {
return this.getErrorsFromValidationError(error);
}
};
render() {
return (
<React.Fragment>
<h1>Login</h1>
<Formik
initialValues={this.initialValues}
validate={this.validate}
validationSchema={this.validationSchema}
onSubmit={this.handleSubmit}
render={({
errors,
touched,
values,
handleBlur,
handleChange,
handleSubmit
}) => (
<form onSubmit={handleSubmit}>
{errors.non_field_errors && (
<formError>{errors.non_field_errors}</formError>
)}
<Label>Username</Label>
<input
onChange={handleChange}
onBlur={handleBlur}
value={values.username}
type="text"
name="username"
placeholder="Enter username"
/>
{touched.username &&
errors.username && <FormError>{errors.username}</FormError>}
<Label>Password</Label>
<input
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
type="password"
name="password"
placeholder="Enter password"
/>
{touched.password &&
errors.password && <FormError>{errors.password}</FormError>}
<button type="submit">Log in</button>
</form>
)}
/>
</React.Fragment>
);
}
Auteur de Formik ici ...
setError
était obsolète en v0.8. et renommé en setStatus
. Vous pouvez utiliser setErrors(errors)
ou setStatus(whateverYouWant)
dans votre fonction handleSubmit
pour obtenir le comportement souhaité, comme ceci:
handleSubmit = async (values, { setErrors, resetForm }) => {
try {
// attempt API call
} catch(e) {
setErrors(transformMyApiErrors(e))
// or setStatus(transformMyApiErrors(e))
}
}
Quelle est la différence? Utilisez setStatus
contre setErrors
?
Si vous utilisez setErrors
, vos erreurs seront effacées par le prochain appel validate
ou validationSchema
de Formik qui peut être déclenché par la saisie de l'utilisateur (événement de modification) ou le flou d'une entrée. (un événement de flou). Remarque: cela suppose que vous n'avez pas défini manuellement validateOnChange
et validateOnBlur
props sur false
(ils sont true
par défaut).
IMHO setStatus
est réellement idéal ici car il placera le (s) message (s) d'erreur dans une partie distincte de l'état de Formik. Vous pouvez ensuite décider quand et comment afficher ce message à l’utilisateur final.
// 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>) }
Sachez que la présence ou la valeur de status
n'a aucun impact sur la prévention de la soumission du formulaire suivant. Formik n'abandonne que le processus de soumission si la validation échoue .
Je viens de résoudre mon propre problème.
J'avais besoin d'utiliser:
setErrors( errors )
au lieu de:
setErrors({ errors })