Tutoriel React — Chapitre 16

Navigation avec React Router

Chapitre 16 — Navigation avec React Router

Les formulaires sont partout : inscription, connexion, ajout de produit, recherche… En React, un formulaire bien géré repose sur 3 piliers : champs contrôlés , state structuré et validation . Ce chapitre vous montre une méthode claire et réutilisable, adaptée aux débutants.

Objectif : comprendre (pas apprendre par cœur) Niveau : débutant absolu Pratique : mini-exercice à la fin

Plan du chapitre

1) Rappels : champ contrôlé

Un champ contrôlé signifie : la valeur du champ vient du state. Chaque frappe déclenche onChange, qui met à jour le state.

import { useState } from "react";

export default function App() {
  const [name, setName] = useState("");

  return (
    <label>
      Prénom :
      <input value={name} onChange={(e) => setName(e.target.value)} />
    </label>
  );
}

2) Gérer plusieurs champs : 2 stratégies

Quand vous avez 2, 3, 10 champs… vous avez deux approches :

A) Un state par champ

  • Facile à comprendre
  • Devient vite long quand il y a beaucoup de champs
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");

B) Un seul state objet (recommandé)

  • Plus propre et scalable
  • Permet un handleChange générique
const [form, setForm] = useState({ email: "", password: "" });

3) Stratégie recommandée : un seul objet form

On regroupe tous les champs dans un objet. L’important : ne jamais modifier l’objet directement, on crée une copie (immutabilité).

import { useState } from "react";

export default function App() {
  const [form, setForm] = useState({
    firstname: "",
    email: "",
    city: ""
  });

  return (
    <div>
      <p>Prénom : {form.firstname}</p>
      <p>Email : {form.email}</p>
      <p>Ville : {form.city}</p>
    </div>
  );
}

4) Un handleChange générique

Au lieu d’écrire 3 fonctions différentes, on crée une seule fonction qui sait quel champ est en train de changer. Pour cela, on utilise name sur l’input.

A) Les inputs doivent avoir un attribut name

<input name="firstname" value={form.firstname} onChange={handleChange} />
<input name="email" value={form.email} onChange={handleChange} />
<input name="city" value={form.city} onChange={handleChange} />

B) La fonction générique

const handleChange = (e) => {
  const { name, value } = e.target;

  setForm((prev) => ({
    ...prev,
    [name]: value
  }));
};

Ici, [name] est une “clé dynamique” : si name === "email", alors on met à jour form.email.

5) Soumission : onSubmit + preventDefault

On place l’handler sur la balise <form>. On empêche le rechargement avec preventDefault.

const handleSubmit = (e) => {
  e.preventDefault();
  console.log("Données envoyées :", form);
};

6) Validations simples (débutant)

Avant d’envoyer les données, on vérifie quelques règles. Exemple :

  • Prénom obligatoire
  • Email doit ressembler à un email
  • Ville obligatoire
const validate = () => {
  const errors = {};

  if (!form.firstname.trim()) errors.firstname = "Le prénom est requis.";
  if (!form.city.trim()) errors.city = "La ville est requise.";

  const emailOk = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email);
  if (!emailOk) errors.email = "Email invalide.";

  return errors;
};

7) Afficher les erreurs proprement

On stocke les erreurs dans un state séparé errors. Puis on affiche l’erreur sous chaque champ si elle existe.

const [errors, setErrors] = useState({});

const handleSubmit = (e) => {
  e.preventDefault();

  const validationErrors = validate();
  setErrors(validationErrors);

  if (Object.keys(validationErrors).length === 0) {
    console.log("OK, on peut envoyer :", form);
  }
};

Afficher sous un champ

<input name="email" value={form.email} onChange={handleChange} />
{errors.email && <p>{errors.email}</p>}

Ici, l’erreur s’affiche uniquement si errors.email existe.

8) Réinitialiser le formulaire

Après un envoi réussi, vous voudrez souvent remettre les champs à zéro.

const initialForm = { firstname: "", email: "", city: "" };

const [form, setForm] = useState(initialForm);

const resetForm = () => {
  setForm(initialForm);
  setErrors({});
};

9) Bonus : checkbox et select

Checkbox : on lit e.target.checked (un booléen), pas value.

const handleChange = (e) => {
  const { name, type, value, checked } = e.target;

  setForm((prev) => ({
    ...prev,
    [name]: type === "checkbox" ? checked : value
  }));
};

Pour un <select>, on récupère simplement value, comme un input texte.

10) Résumé (à retenir)

  • Un formulaire React “pro” utilise des champs contrôlés.
  • Pour plusieurs champs, un state objet form est très pratique.
  • Un handleChange générique utilise name + [name].
  • Validation simple : fonction validate() → objet errors.
  • On affiche chaque erreur sous le champ correspondant.

11) Exercice pratique

Créez un formulaire “Inscription” avec :

  • Prénom (obligatoire)
  • Email (format email)
  • Ville (obligatoire)
  • Une checkbox “J’accepte les conditions” (obligatoire)

Contraintes

  • Utilisez un seul state objet pour les champs.
  • Utilisez un handleChange générique.
  • Affichez les erreurs sous chaque champ.
  • Si tout est OK, affichez un message “Inscription réussie” et réinitialisez le formulaire.

Prochaine étape : Chapitre 17 — Introduire le Context API pour éviter le “props drilling” quand l’application grandit.