Article original : How to Validate Forms in React and React Native Using Yup and Formik
La validation est une partie clé du développement, quel que soit le langage de programmation que vous utilisez. Les développeurs doivent toujours valider les entrées utilisateur, les paramètres d'API et les valeurs récupérées.
L'un des éléments les plus courants où vous devrez appliquer la validation des entrées utilisateur est via un formulaire. Cela pourrait être un formulaire d'inscription utilisateur, un formulaire de contact ou un simple questionnaire.
Résultats du tutoriel
À la fin de cet article, vous serez capable de :
- Comprendre les problèmes courants avec la validation de formulaires.
- Comment utiliser la bibliothèque de validation de schéma Yup avec la bibliothèque de formulaires Formik.
- Comment construire un formulaire dans React avec une validation complète (les mêmes principes s'appliquent pour React Native, avec une syntaxe de composant différente).
Contenu
- Qu'est-ce que la validation ?
- L'objet à valider
- Présentation de Yup et Formik
- Comment ajouter la validation à un formulaire
- Conclusion
Qu'est-ce que la validation ?
La validation est définie comme :
l'action de vérifier ou de prouver la validité ou l'exactitude de quelque chose.
Mais que signifie cela en langage informatique ? Cela pourrait être une multitude de choses, mais le principe reste le même. Vous pourriez valider une valeur de variable ou un objet par rapport à un ensemble de règles ou de réglementations prédéterminées.
Des exemples de règles de validation pourraient être :
- Le mot de passe doit comporter au moins 8 caractères et contenir un caractère spécial.
- Le nom d'utilisateur doit être unique.
- La date de naissance doit être reçue sous forme de chaîne, dans un format particulier, par exemple ISO8601.
Prenons l'exemple d'un formulaire d'inscription utilisateur sur un site web.
L'objet à valider
Le formulaire comprendra plusieurs entrées pour former un objet UserRegistration. Comme ceci :
interface UserRegistration {
firstName: string;
surname: string;
email: string;
dob: string;
}
Ci-dessus se trouve une interface (contrat) pour un objet UserRegistration. Elle définit simplement certaines informations clés sur l'utilisateur qui doivent être collectées, avec toutes les valeurs étant de type string.
Bien que des langages comme TypeScript soient utiles pour garantir que nous passons les types corrects aux fonctions dans notre application, ils ne valident pas intrinsèquement le contenu ou les valeurs réels de ces types. TypeScript garantit qu'une variable est d'un type spécifique, comme une chaîne ou un nombre, mais il ne vérifie pas si le contenu de cette chaîne ou de ce nombre répond à des critères ou des contraintes spécifiques.
Que se passe-t-il si nous ne validons pas les valeurs ?
Avant de passer à la validation, examinons ce qui pourrait se passer si nous ne validons pas.
Sans validation, un utilisateur pourrait saisir les valeurs suivantes :
Prénom : 1231301
Nom : Hello##test_101
Email : user_123@@email.to@.com
Date de naissance : 10+12+1909
Ces valeurs peuvent sembler parfaitement acceptables, et votre front-end peut les autoriser à être soumises sans aucun problème. Et l'API les acceptera probablement initialement.
Mais lorsque l'API tente d'analyser ces valeurs (conversion) lors du traitement de la demande, elle rencontrera des erreurs et ne parviendra pas à traiter la demande correctement.
Il y a plusieurs conséquences négatives à cette approche :
- Charge serveur accrue : Le front-end effectue plusieurs demandes invalides au serveur, ce qui sollicite inutilement le serveur. Cette charge supplémentaire aurait pu être évitée.
- Coûts potentiellement plus élevés : Le coût de traitement de ces demandes invalides peut augmenter considérablement, selon votre plan d'hébergement et la configuration du serveur. Chaque demande invalide consomme des ressources serveur qui pourraient être utilisées plus efficacement.
- Mauvaise expérience utilisateur (UX) : Les utilisateurs risquent de devenir frustrés s'ils saisissent à plusieurs reprises des détails, soumettent le formulaire, puis reçoivent des messages d'erreur indiquant que leurs entrées sont invalides. Cela peut conduire à une perception négative de l'application.
Pour atténuer ces problèmes et réduire le nombre de demandes invalides, nous pouvons implémenter une 'validation côté client' pour nous assurer que les données répondent aux critères requis avant d'envoyer la demande à l'API.
Présentation de Yup et Formik
Yup et Formik sont deux bibliothèques que vous pouvez ajouter à toute application React ou React Native via npm ou yarn.
Yup est une bibliothèque de construction de schémas qui vous permet de créer des schémas pour la validation à l'exécution. Elle dispose d'une multitude de fonctions d'extension qui peuvent définir des ensembles de règles, transformer des valeurs et retourner des messages de validation directement.
Examinons un exemple de schéma Yup pour notre formulaire utilisateur :
import * as Yup from 'yup';
// Si vous utilisez TypeScript, vous pouvez utiliser une fonction wrapper pour imposer un typage strict
const createYupSchema = <T extends object>(schema: Yup.ObjectSchema<T>): Yup.ObjectSchema<T> => schema;
export const userFormSchema = createYupSchema<UserInput>(
Yup.object().shape({
firstname: Yup.string().required('Le prénom est requis'),
surname: Yup.string().required('Le nom est requis'),
email: Yup.string().email('Format d\'email invalide').required('L\'email est requis'),
dob: Yup.string().required('La date de naissance est requise'),
})
);
// Version JS
export const validationSchema = Yup.object({
firstname: Yup.string().required('Le prénom est requis'),
surname: Yup.string().required('Le nom est requis'),
email: Yup.string().email('Format d\'email invalide').required('L\'email est requis'),
dob: Yup.date().required('La date de naissance est requise')
});
Nous créons un objet Yup (schéma) qui contient toutes nos clés pour notre interface UserInput.
Parties du schéma :
- clé : la clé qui sera utilisée plus tard pour le nom de notre élément (comme nous utilisons TypeScript, cela doit correspondre au nom de la clé de l'objet).
- ensemble de règles : pour toutes les clés, appliquer un ensemble de règles. Un ensemble de règles doit commencer par une déclaration de typage, c'est-à-dire
Yup.string()ouYup.number()et ainsi de suite. Vous pouvez ensuite enchaîner vos autres fonctions de validation.
L'utilisation de TypeScript garantit que nous faisons correspondre le type de schéma à nos types d'interface.
Par exemple, si nous essayons de faire ceci :
firstname: Yup.date().required();
cela générera une erreur TypeScript se plaignant que firstname ne peut pas être validé comme s'il s'agissait d'une date, car le type de firstname est une string.
Comment ajouter la validation à un formulaire
C'est là que notre bibliothèque Formik intervient et rend les choses beaucoup plus faciles que la validation d'un formulaire et la mise en œuvre de la gestion des erreurs manuellement.
Formik est une bibliothèque qui encapsule un composant <Form/>. Elle nous permet de créer des formulaires plus riches dans React et React Native, nous donnant accès à des fonctionnalités comme l'état du formulaire, la gestion des erreurs, la validation et le traitement des soumissions de formulaires beaucoup plus efficacement.
Vous pouvez accéder à une version pré-construite d'un UserForm utilisant Yup, Formik et React (Vite) sur mon GitHub ici. Il suffit de cloner le dépôt GitHub et de suivre les instructions du README.md.
<Formik
initialValues={initialValues}
validationSchema={userFormSchema}
onSubmit={onSubmit}
>
{({ isValid, dirty, isSubmitting }) => (
<Form>
<div className="form-control">
<label htmlFor="firstName">Prénom</label>
<Field type="text" id="firstName" name="firstName" />
<ErrorMessage name="firstName" component="div" className="error" />
</div>
<div className="form-control">
<label htmlFor="surname">Nom</label>
<Field type="text" id="surname" name="surname" />
<ErrorMessage name="surname" component="div" className="error" />
</div>
<div className="form-control">
<label htmlFor="email">Email</label>
<Field type="email" id="email" name="email" />
<ErrorMessage name="email" component="div" className="error" />
</div>
<div className="form-control">
<label htmlFor="dob">Date de naissance</label>
<Field type="date" id="dob" name="dob" />
<ErrorMessage name="dob" component="div" className="error" />
</div>
<button type="submit" disabled={isSubmitting || !isValid}>Soumettre</button>
</Form>
)}
</Formik>
Dans ce code, nous avons utilisé le composant de bibliothèque <Formik/>, qui enveloppe notre élément <Form/> standard. Nous passons les propriétés suivantes au composant :
**initialValues**– ce sont les valeurs initiales requises de votre formulaire (c'est-à-dire lorsque le formulaire est rendu, quelles valeurs vos entrées auront).validationSchema– c'est probablement le plus important pour ce tutoriel. Gardez à l'esprit que c'est une propriété optionnelle, car elle n'est pas nécessaire pour utiliser le composant<Formik/>, mais pour toute validation, elle l'est.
Nous allons importer notreuserFormSchemaque nous avons créé dans l'étape précédente. Cela va dire au formulaire, lors de la validation des entrées dans ce formulaire, d'utiliser ces schémas.onSubmit– une fonction simple à exécuter lors du clic sur votre bouton / soumission du formulaire. Les valeurs du formulaire seront automatiquement passées à cette fonction.
Vous pouvez envelopper le formulaire dans une fonction "render prop" pour utiliser certaines des propriétés exposées par Formik dans votre formulaire. Vous pouvez en apprendre davantage sur les render props ici.
{({ isValid, isSubmitting }) => (
Note : Cela n'est pas requis si vous ne souhaitez pas utiliser les propriétés sous-jacentes de Formik dans le formulaire lui-même. Vous pouvez simplement supprimer et placer votre balise d'ouverture <Form> à sa place.
Mais l'utilisation de cette render prop vous permet d'accéder aux propriétés exposées par le composant Formik dans votre élément <Form/>. Vous pouvez voir que nous utilisons les propriétés isValid et isSubmitting pour contrôler l'état de notre bouton de soumission.
En continuant avec l'analyse du code :
**isValid**– une valeur booléenne que Formik contrôle en fonction du résultat de la validation de notre schéma.**isSubmitting**– Un indicateur booléen indiquant si un formulaire est en cours de soumission. Cet indicateur est très utile lorsque vous souhaitez désactiver un bouton, pour éviter plusieurs clics signifiant plusieurs soumissions du formulaire.
Nous pouvons utiliser ces valeurs pour contrôler l'activation du bouton de soumission comme suit :
<button type="submit" disabled={isSubmitting || !isValid}>Soumettre</button>
Champs de saisie
Il est important de noter que lors de l'utilisation de Formik et Yup, pour que la validation fonctionne, les noms des champs de saisie doivent correspondre exactement aux clés du schéma Yup (sensible à la casse) – sinon les règles de validation ne seront pas enregistrées.
Exemple :
<Field type="email" id="email" name="email" />
<ErrorMessage name="email" component="div" className="error" />
Nous avons défini ce champ pour être utilisé pour une saisie d'email, et lui avons donné le name correspondant "email" à notre définition de userFormSchema.
En dessous, nous codons notre composant Formik <ErrorMessage/>, en passant à nouveau le nom 'email', correspondant à notre schéma. En utilisant la propriété name, nous sommes capables de lier notre saisie, message d'erreur et schémas de validation ensemble.
Si des problèmes surviennent lors de la validation du champ de saisie, le message d'erreur affichera les messages d'erreur définis – sinon, il reviendra à un message par défaut, par exemple "firstname est un champ requis". Cela peut être moins convivial, donc je recommande toujours de passer un message personnalisé.
Vous remarquerez également que lorsque nous perdons le focus ou lors de la frappe (après que la première validation a été exécutée), elle exécutera automatiquement la validation à nouveau. Vous pouvez remplacer cette fonctionnalité en définissant les indicateurs validateOnBlur et validateOnChange (true / false).
Par exemple, cela ressemblera à ceci dans son état d'erreur :
Image : Formulaire Formik invalide montrant l'état d'erreur
Une fois que nous avons saisi des valeurs pour toutes les entrées et que notre validation a réussi (vous pouvez voir que le bouton de soumission est maintenant activé), nous pouvons soumettre.
Image : Formulaire utilisateur valide avec bouton de soumission activé
Validation supplémentaire et fonctionnalités de Formik
Vous avez maintenant vu à quel point Yup et Formik peuvent faciliter la création d'un formulaire. Il dispose d'une validation complète et même d'une gestion des erreurs, ce qui signifie que vous pouvez avoir un formulaire entièrement fonctionnel et convivial construit en quelques minutes.
Mais que se passe-t-il si vous souhaitez ajouter une validation plus complexe à un formulaire beaucoup plus grand / compliqué ? Eh bien, examinons un exemple :
Supposons que nous voulons valider que la date de naissance fournie garantit que l'utilisateur a plus de 18 ans. Nous ajouterons également un champ de mot de passe, qui aura les règles suivantes :
- minimum de 6 lettres
- contenir un nombre
- contenir un caractère spécial
Exigences supplémentaires pour la date de naissance
Nous pouvons faire cela en enchaînant la fonction test() à la fonction string() de l'objet dob dans notre schéma.
La fonction test() nous permet de tester une logique personnalisée. Mettez à jour le paramètre dob dans le userFormSchema comme suit :
dob: Yup.string()
.required('La date de naissance est requise')
.test('is-older-than-18', 'Vous devez avoir au moins 18 ans', (value) => {
if (!value) return false;
// essayer d'analyser la valeur en date
const parsedDate = parse(value, 'yyyy-MM-dd', new Date());
if (!isValid(parsedDate)) return false;
const today = new Date();
const eighteenYearsAgo = subYears(today, 18);
// vérifier si la date fournie est antérieure ou identique à il y a 18 ans.
return parsedDate <= eighteenYearsAgo;
})
Nous obtenons maintenant l'erreur suivante lorsque nous essayons de soumettre une date qui est inférieure à 18 ans.
Image : Champ de saisie invalide en raison d'une validation de date échouée
Validation du mot de passe
Pour la validation du champ de mot de passe, nous pouvons faire quelque chose comme ceci :
password: Yup.string()
.required('Ce champ est requis')
.min(6, 'Doit comporter au moins 6 caractères')
.matches(/[!@#$%^&*(),.?":{}|<>]/, 'Doit contenir au moins un caractère spécial')
.matches(/\d/, 'Doit contenir au moins un nombre');
Ici, nous utilisons la fonction matches(), en passant une expression régulière à vérifier. Vous pourriez combiner ces cas en une seule expression régulière, mais l'avantage de les garder séparés est qu'il permet de pointer quelle règle de validation échoue. Cela permet également un message d'erreur plus granulaire et une maintenance si les règles changent à l'avenir.
Autres méthodes utiles :
length()– vérifie la longueur de la chaîne / du nombrepositive()– vérifie que le type de nombre est un nombre positifemail()– vérifie qu'il s'agit d'une adresse email valideurl()– vérifie qu'il s'agit d'une URL validemin()/max()– vérifie que le nombre est au moins 'x' et inférieur à 'y'ensure()– transforme les valeursundefinedetnullen une chaîne vide tout en définissant la valeurdefaultà une chaîne vide.
Conclusion
Comme vous pouvez le voir, les possibilités avec Yup sont vastes. Combinez cela avec la bibliothèque Formik, et vous pouvez avoir des formulaires riches, efficaces et faciles à utiliser.
Cette facilité d'utilisation permet de mettre en place un formulaire beaucoup plus rapidement sur votre application web ou mobile, vous permettant de vous concentrer sur l'expérience utilisateur, le design et la logique métier.
Comme toujours, n'hésitez pas à me contacter et à discuter de cet article avec moi sur Twitter, et n'oubliez pas de me suivre pour entendre parler des futurs articles et conseils de développement.