frontend / react
Gestion d'événements
Explication
Ce que vous allez apprendre
- Associer un handler à un événement DOM avec la syntaxe
onEvent={fonction} - Distinguer un événement synthétique React d'un événement natif du navigateur
- Utiliser
preventDefault()etstopPropagation()à bon escient - Passer un argument à un handler sans l'exécuter au moment du rendu
- Repérer le piège classique
onClick={fn()}avant qu'il ne casse la page
Dans quel contexte ?
Un développeur ajoute un bouton "Supprimer" dans src/components/TaskItem.jsx. Au rechargement de la page, toutes les tâches disparaissent instantanément sans qu'aucun clic n'ait eu lieu. La cause : onClick={onSupprimer(tache.id)} au lieu de onClick={() => onSupprimer(tache.id)} — la fonction est appelée immédiatement pendant le rendu, pas au clic. C'est l'un des bugs les plus fréquents chez les développeurs qui découvrent React.
Étape 1 : réagir aux actions de l'utilisateur
Un événement est une action déclenchée par l'utilisateur : un clic, une frappe au clavier, une soumission de formulaire. C'est le rôle des "handlers" de réagir à ces actions.
Étape 2 : le problème historique des navigateurs
Historiquement, chaque navigateur avait ses propres bizarreries dans la façon de représenter les événements du DOM, ce qui compliquait le code multi-navigateur.
Étape 3 : la solution de React, les événements synthétiques
React a introduit son propre système d'événements, dits "synthétiques", qui enveloppe les événements natifs dans une API unifiée. On écrit son code une seule fois (onClick, onChange) sans se soucier des différences entre navigateurs.
Étape 4 : comment associer un handler
On associe un handler à un élément JSX via un attribut commençant par "on" suivi du nom de l'événement en camelCase. Ce handler reçoit un objet événement donnant accès à des informations utiles.
Étape 5 : deux méthodes à connaître
Deux méthodes reviennent très souvent : preventDefault(), qui empêche le comportement par défaut du navigateur, et stopPropagation(), qui empêche l'événement de remonter vers les gestionnaires des éléments parents.
Étape 6 : le piège le plus fréquent
Il reste un piège à éviter absolument : un handler passé en prop doit toujours être une référence de fonction, jamais un appel direct. Écrire onClick={maFonction()} exécute la fonction immédiatement au rendu, pas au clic.
Piège fréquent
onClick={supprimer(id)} exécute supprimer(id) à CHAQUE rendu du composant, pas au clic — souvent en boucle infinie si supprimer modifie un state. La règle simple : ce qui suit on...= doit toujours être une fonction, jamais son résultat.
Comment passer un argument malgré tout
Quand on a besoin de passer un argument à un handler, il faut l'envelopper dans une fonction fléchée : onClick={() => maFonction(id)}, qui ne s'exécute qu'au moment voulu.
| Événement | Attribut JSX | Cas d'usage typique |
|---|---|---|
| Clic souris | onClick | Bouton, lien, carte cliquable |
| Changement de champ | onChange | Input contrôlé, select |
| Soumission de formulaire | onSubmit | Formulaire de connexion, recherche |
| Touche clavier | onKeyDown | Valider avec Entrée, fermer avec Échap |
Et ensuite ?
Une fois les événements maîtrisés, la prochaine étape consiste à afficher des collections de données avec les listes et leurs clés.
Commandes & code
Gestion d'événements
React utilise un système d'événements synthétiques, unifié entre navigateurs.
function Formulaire() {
function gererClic(e) {
console.log("Bouton cliqué", e.target);
}
function gererSubmit(e) {
e.preventDefault(); // empêche le rechargement de page par défaut
console.log("Formulaire soumis");
}
return (
<form onSubmit={gererSubmit}>
<button onClick={gererClic}>Cliquer</button>
{/* onClick reçoit une RÉFÉRENCE de fonction, jamais un appel : onClick={gererClic()} = bug */}
</form>
);
}// Passer des arguments à un handler : wrapper avec une arrow function
function ListeProduits({ produits, onSupprimer }) {
return (
<ul>
{produits.map((p) => (
<li key={p.id}>
{p.nom}
<button onClick={() => onSupprimer(p.id)}>Supprimer</button>
{/* onClick={onSupprimer(p.id)} exécuterait la fonction IMMÉDIATEMENT au rendu -- bug fréquent */}
</li>
))}
</ul>
);
}// Propagation d'événements : stopPropagation, comme en DOM classique
function CarteCliquable() {
function ouvrirDetail() {
console.log("Ouvre le détail de la carte");
}
function gererFavori(e) {
e.stopPropagation(); // empêche l'événement de remonter jusqu'à onClick de la carte
console.log("Ajouté aux favoris");
}
return (
<div className="carte" onClick={ouvrirDetail}>
<button onClick={gererFavori}>Favori</button>
</div>
);
}// Événements clavier, souris et formulaire les plus courants
function ChampRecherche() {
const [valeur, setValeur] = useState("");
function gererTouche(e) {
if (e.key === "Enter") {
console.log("Recherche :", valeur);
}
if (e.key === "Escape") {
setValeur("");
}
}
return (
<input
value={valeur}
onChange={(e) => setValeur(e.target.value)}
onKeyDown={gererTouche}
onFocus={() => console.log("focus")}
onBlur={() => console.log("blur")}
/>
);
}
// Événements synthétiques sont poolés historiquement (React < 17), mais depuis React 17+
// ils sont utilisables de façon asynchrone sans précaution particulière (e.persist() obsolète)
function GlisserDeposer() {
function gererDrop(e) {
e.preventDefault();
const donnees = e.dataTransfer.getData("text/plain");
console.log("Déposé :", donnees);
}
return (
<div onDragOver={(e) => e.preventDefault()} onDrop={gererDrop}>
Zone de dépôt
</div>
);
}Résumé
- Un handler passé en prop est toujours une RÉFÉRENCE de fonction, jamais un appel direct dans le JSX.
- Passer un argument à un handler nécessite une arrow function englobante :
onClick={() => fn(id)}. e.stopPropagation()empêche un événement de remonter aux gestionnaires des ancêtres.- Les événements synthétiques React unifient le comportement entre navigateurs sur une API commune.
Exercices pratiques
Mission : stopper la suppression fantôme des tâches
Objectif : Corriger un handler exécuté au rendu au lieu du clic, et sécuriser un clic imbriqué avec stopPropagation.
Contexte
Sur TaskItem.jsx, dès que la page se charge, toutes les tâches disparaissent instantanément sans qu'aucun clic n'ait eu lieu. Le code contient <button onClick={onSupprimer(tache.id)}>Supprimer</button>. Ce composant TaskItem est aussi cliquable dans son ensemble pour ouvrir un détail (onClick={ouvrirDetail} sur le <div> englobant), et un bouton "Favori" à l'intérieur déclenche par erreur l'ouverture du détail en plus de son propre effet.