useState et événements
Répondre à une question : un clic, une mémoire, un écran qui se met à jour.
Objectifs de la séance
- comprendre pourquoi une variable ordinaire ne met pas l'écran à jour ;
- utiliser
useState(valeur, fonction de modification, valeur initiale) ; - réagir à un clic avec
onClick; - appliquer la règle d'or : remplacer l'état, ne jamais le modifier ;
- utiliser la mise à jour fonctionnelle
setX((ancien) => ...); - protéger l'interface contre le double clic.
Petite vérification
Avant de commencer, vérifiez si vous savez...
- lancer
npm run devet compter les cartes affichées dans le navigateur - dire pourquoi on écrit
key={reponse.id}plutôt quekey={index}dans unmap - dans
data/questions.ts, remplacer le niveau1de la première question par4et lire l'erreur affichée par l'éditeur
📌 Réponse attendue
Notions théoriques
Le problème : une variable ne suffit pas
Essayons un compteur avec une variable ordinaire :
export function Compteur() {
let score = 0;
return (
<div>
<p>Score : {score}</p>
<button onClick={() => { score = score + 1; }}>+1</button>
</div>
);
}
La variable change bien, mais l'écran reste à 0 : React ne sait pas qu'il doit se redessiner. De plus, let score = 0 serait remise à 0 à chaque nouvel affichage.
La solution : useState
useState donne au composant une mémoire qui survit aux affichages et qui prévient React quand elle change.
import { useState } from "react";
export function Compteur() {
const [score, setScore] = useState(0);
return (
<div>
<p>Score : {score}</p>
<button onClick={() => setScore(score + 1)}>+1</button>
</div>
);
}
useState(0)renvoie un tableau de deux éléments, que l'on récupère par destructuration (comme dans le cours JavaScript) ;scoreest la valeur actuelle ;setScoreest la fonction qui la remplace ;0est la valeur initiale, utilisée seulement au premier affichage.
À chaque clic : setScore remplace la valeur, React rappelle la fonction du composant (on parle de rendu) avec la nouvelle valeur, puis met à jour la page.
Les événements
En React, on attache un gestionnaire d'événement avec un attribut en camelCase : onClick, onChange, onSubmit... Il reçoit une fonction.
<button onClick={() => choisir(reponse)}> {/* JUSTE : on donne une fonction */}
<button onClick={choisir(reponse)}> {/* FAUX : appelle choisir tout de suite, au rendu */}
Chaque rendu a sa propre valeur
Dans un rendu donné, score est une photo : il ne change pas, même après setScore.
function ajouterDeux() {
setScore(score + 1); // score vaut 0 dans ce rendu : on demande 0 + 1
setScore(score + 1); // toujours 0 : on demande à nouveau 0 + 1
}
// Résultat : le score passe de 0 à 1, et non à 2 !
Quand le nouvel état dépend de l'ancien, on passe une fonction à setScore : elle reçoit toujours la dernière valeur.
function ajouterDeux() {
setScore((ancien) => ancien + 1);
setScore((ancien) => ancien + 1);
}
// Résultat : le score passe de 0 à 2
Ne jamais modifier, toujours remplacer
React détecte un changement en comparant l'ancienne et la nouvelle valeur. Un tableau ou un objet modifié « en place » reste le même objet : React ne voit rien.
const [themes, setThemes] = useState<string[]>([]);
themes.push("réseaux"); // FAUX : modifie l'original, l'écran ne bouge pas
setThemes((ancien) => [...ancien, "réseaux"]); // JUSTE : un nouveau tableau (spread)
Les règles des hooks
useState est un hook (son nom commence par use). Deux règles :
- il s'appelle tout en haut du composant, jamais dans un
if, une boucle ou une fonction imbriquée ; - il s'appelle uniquement dans un composant (ou dans un autre hook).
Ne pas stocker ce qui se calcule
Le pourcentage se déduit du score et du nombre de questions : on le calcule à chaque rendu au lieu de le stocker dans un état. Deux états qui doivent rester synchronisés finissent toujours par se contredire.
Se protéger du double clic
Un joueur impatient peut cliquer deux fois très vite. Une garde en début de fonction ignore le second clic, et l'attribut disabled empêche d'insister :
function choisir(reponse: Reponse) {
if (choix !== null) {
return; // une réponse a déjà été donnée
}
setChoix(reponse.id);
}
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Votre mission : transformer la liste de questions en une vraie partie : une question à la fois, une correction immédiate, un score.