Cours React Native · Module 5: Formulaires

Validation avec Yup et Zod

3 min de lecture ·
Dans cette leçon4

Les schémas de validation te permettent de définir les règles en un seul endroit.

Yup

npx expo install yup @hookform/resolvers
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';

// Schéma de validation
const schema = yup.object({
  name: yup
    .string()
    .required('Le nom est obligatoire')
    .min(2, 'Min. 2 caractères')
    .max(50, 'Max. 50 caractères'),

  email: yup
    .string()
    .required('L\'email est obligatoire')
    .email('Format d\'email invalide'),

  password: yup
    .string()
    .required('Le mot de passe est obligatoire')
    .min(8, 'Min. 8 caractères')
    .matches(/[a-z]/, 'Minuscule requise')
    .matches(/[A-Z]/, 'Majuscule requise')
    .matches(/[0-9]/, 'Chiffre requis'),

  confirmPassword: yup
    .string()
    .required('Confirme le mot de passe')
    .oneOf([yup.ref('password')], 'Les mots de passe doivent correspondre'),

  age: yup
    .number()
    .transform((value) => (isNaN(value) ? undefined : value))
    .required('L\'âge est obligatoire')
    .min(18, 'Tu dois avoir au moins 18 ans')
    .max(120, 'Âge invalide'),

  terms: yup
    .boolean()
    .oneOf([true], 'Tu dois accepter les conditions'),
});

type FormData = yup.InferType<typeof schema>;

function YupForm() {
  const {
    control,
    handleSubmit,
    formState: { errors },
  } = useForm<FormData>({
    resolver: yupResolver(schema),
    defaultValues: {
      name: '',
      email: '',
      password: '',
      confirmPassword: '',
      age: undefined,
      terms: false,
    },
  });

  return (
    <View>
      {/* Champs du formulaire avec Controller */}
    </View>
  );
}

Zod (alternative)

npx expo install zod @hookform/resolvers
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';

const schema = z.object({
  name: z
    .string()
    .min(1, 'Le nom est obligatoire')
    .min(2, 'Min. 2 caractères'),

  email: z
    .string()
    .min(1, 'L\'email est obligatoire')
    .email('Email invalide'),

  password: z
    .string()
    .min(8, 'Min. 8 caractères')
    .regex(/[a-z]/, 'Minuscule requise')
    .regex(/[A-Z]/, 'Majuscule requise')
    .regex(/[0-9]/, 'Chiffre requis'),

  confirmPassword: z.string(),

  website: z
    .string()
    .url('URL invalide')
    .optional()
    .or(z.literal('')),
}).refine((data) => data.password === data.confirmPassword, {
  message: 'Les mots de passe doivent correspondre',
  path: ['confirmPassword'],
});

type FormData = z.infer<typeof schema>;

function ZodForm() {
  const { control, handleSubmit } = useForm<FormData>({
    resolver: zodResolver(schema),
  });

  // ...
}

Validation conditionnelle

const schema = yup.object({
  hasCompany: yup.boolean(),

  companyName: yup.string().when('hasCompany', {
    is: true,
    then: (schema) => schema.required('Nom de l\'entreprise requis'),
    otherwise: (schema) => schema.notRequired(),
  }),

  taxId: yup.string().when('hasCompany', {
    is: true,
    then: (schema) =>
      schema
        .required('Numéro fiscal requis')
        .matches(/^\d{10}$/, 'Le numéro fiscal doit comporter 10 chiffres'),
  }),
});

Validation asynchrone

const schema = yup.object({
  username: yup
    .string()
    .required()
    .test('unique', 'Nom d\'utilisateur déjà pris', async (value) => {
      if (!value) return true;
      const response = await fetch(`/api/check-username?name=${value}`);
      const { available } = await response.json();
      return available;
    }),
});

Les schémas de validation sont une façon lisible et réutilisable de définir les règles !

Code de cette leçon : App.tsx
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { z } from 'zod';

// TODO : crée un schéma de validation avec z.object({ ... }) :
//   - email: z.string().email('Email invalide')
//   - password: z.string().min(8, 'Min. 8 caractères')
//   - confirmPassword: z.string()
// Ajoute .refine(...) qui vérifie si password === confirmPassword
//   avec message: 'Les mots de passe doivent correspondre' et path: ['confirmPassword']

// TODO : dérive le type à partir du schéma : type FormData = z.infer<typeof schema>

export default function App() {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>
        Complète le schéma de validation Zod ci-dessus.
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    justifyContent: 'center',
    backgroundColor: '#1a5276',
  },
  text: {
    fontSize: 16,
    color: '#aed6f1',
    textAlign: 'center',
  },
});

Entrées de la documentation utilisées dans cette leçon

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Comment connecte-t-on Yup à react-hook-form ?

  2. 2. Quelle bibliothèque offre le meilleur support de TypeScript ?

Tâches pratiques dans le jeu

  • Clique dans l'ordre

    Clique les éléments d'un schéma email en Yup :

  • Éditeur de code

    Complète le schéma avec la validation de l'email, du mot de passe et de sa confirmation.

Articles utiles