State & Événements
Rends tes composants vivants et interactifs au fil du jeu.
3.1Pourquoi une variable classique ne suffit pas
Si tu crées une variable locale classique dans ton composant et que tu essaies de la modifier lors d'un clic, il ne se passera rien à l'écran :
// ❌ Pourquoi ça ne marche pas :
function CompteurInactif() {
let score = 0; // variable locale classique
const incrementer = () => {
score++;
console.log("Score en mémoire :", score); // Le score change en mémoire...
};
return (
<div>
<p>Score : {score}</p> {/* ...mais React ne redessine pas le composant ! */}
<button onClick={incrementer}>+1</button>
</div>
);
}React ne surveille pas tes variables locales. Il n'a aucun moyen de savoir qu'elles ont changé, et il ne déclenchera pas de mise à jour visuelle. Pour cela, tu dois utiliser un **Hook** spécial appelé useState.
3.2Le Hook useState : enregistrer ses stats auprès de React
Un "Hook" est une fonction spéciale de React qui permet de "se brancher" sur ses fonctionnalités internes. useState permet de déclarer une variable d'état que React va surveiller de près.
import { useState } from "react";
function CompteurInteractif() {
// useState renvoie toujours un tableau avec 2 éléments :
// 1. La valeur actuelle de ton état
// 2. La fonction "setter" pour modifier cette valeur
const [score, setScore] = useState(0); // 0 est la valeur de départ
return (
<div>
<p>Score actuel : {score}</p>
{/* On utilise obligatoirement le setter pour modifier la valeur */}
<button onClick={() => setScore(score + 1)}>
+1 Point
</button>
</div>
);
}Règle de fer : N'écris JAMAIS score = score + 1. Modifie toujours ton état en passant par son setter (setScore). Si tu tentes de modifier directement la variable, React ne détectera pas le changement, et l'écran restera figé.
3.3Sous le capot : le cycle de rendu (Trigger - Render - Commit)
Pour bien comprendre pourquoi ton écran se met à jour, il faut visualiser le cycle en 3 étapes que React exécute à chaque changement d'état :
Tu appelles la fonction modificatrice, par exemple
setScore(score + 1). C'est le signal de départ pour React.React appelle à nouveau ton composant. Il exécute la fonction pour voir à quoi ressemble la nouvelle structure de JSX retournée en fonction du nouveau score.
React compare le nouveau JSX avec l'ancien, identifie ce qui a changé, et applique uniquement ces changements sur le DOM réel du navigateur. L'affichage se met à jour.
3.4Les événements en React : interagir avec l'utilisateur
Les événements React fonctionnent de façon très similaire aux événements HTML standards, mais s'écrivent en camelCase (comme onClick, onChange, onSubmit). Tu leur passes une référence de fonction à exécuter :
function Arena() {
const attaquer = () => {
alert("Coup d'épée lancé ! ⚔️");
};
return (
<div>
{/* 1. Référence de fonction : on donne le nom de la fonction,
React l'exécutera lors du clic. */}
<button onClick={attaquer}>Attaquer</button>
{/* 2. Fonction inline : pratique pour des lignes courtes */}
<button onClick={() => console.log("Potion bue")}>
Boire potion
</button>
</div>
);
}Le piège classique : N'écris jamais onClick={attaquer()} avec des parenthèses dans ton JSX. Si tu fais cela, la fonction s'exécutera immédiatement pendant le rendu initial de la page, avant même que l'utilisateur ne clique sur quoi que ce soit. Passe toujours la référence de la fonction : onClick={attaquer}.
3.5Les formulaires contrôlés : la source unique de vérité
En HTML classique, c'est le navigateur qui stocke la valeur de ce que tu tapes dans un champ de texte. En React, on préfère que ce soit le **state** qui contrôle tout. C'est le concept de **formulaire contrôlé** :
import { useState } from "react";
function InscriptionGuilde() {
const [pseudo, setPseudo] = useState("");
const validerFormulaire = (e: React.FormEvent) => {
e.preventDefault(); // Empêche le navigateur de recharger la page
console.log("Bienvenue dans la guilde,", pseudo);
};
return (
<form onSubmit={validerFormulaire}>
<label>Choisis ton pseudo :</label>
<input
type="text"
// 1. La valeur affichée est dictée par le state
value={pseudo}
// 2. À chaque touche tapée, on met à jour le state
onChange={(e) => setPseudo(e.target.value)}
placeholder="Ex: Legolas"
/>
<button type="submit">Rejoindre</button>
</form>
);
}3.6Immutabilité : modifier des tableaux et objets dans le State
React compare l'ancien et le nouvel état en vérifiant leur **référence en mémoire** (par égalité stricte `===`). Si tu modifies directement le contenu d'un tableau ou d'un objet existant, sa référence en mémoire ne change pas. React pensera que l'état n'a pas bougé et refusera de mettre à jour l'écran !
C'est la règle de l'**immutabilité**. Tu dois toujours créer une **copie complète** de ton tableau ou de ton objet avant de l'envoyer au setter. On utilise pour cela le spread operator (...) :
import { useState } from "react";
interface Item {
id: number;
nom: string;
}
function Inventaire() {
const [sac, setSac] = useState<Item[]>([
{ id: 1, nom: "Épée longue" },
{ id: 2, nom: "Bouclier en bois" }
]);
// ✅ CORRECT : Ajouter un item (on crée un nouveau tableau)
const ajouterItem = (nomNouvelItem: string) => {
const nouvelItem = { id: Date.now(), nom: nomNouvelItem };
setSac([...sac, nouvelItem]); // On déverse l'ancien sac + le nouvel item
};
// ✅ CORRECT : Supprimer un item (filter renvoie un nouveau tableau)
const jeterItem = (idAArroser: number) => {
setSac(sac.filter(item => item.id !== idAArroser));
};
// ❌ INCORRECT (ne provoquera aucun rendu) :
// sac.push({ id: 3, nom: "Potion" });
// setSac(sac); // La référence mémoire du tableau 'sac' n'a pas changé !
}