React : Le state et les hooks
Donner de la mémoire à un composant avec useState : l'état qui déclenche le re-rendu, la règle d'or de l'immutabilité, et les règles des hooks. Le cœur de l'interactivité React.
React : Le state et les hooks
On a vu que les props sont en lecture seule : un composant les reçoit, il ne les change pas. Mais une interface, ça bouge : un compteur s'incrémente, un menu s'ouvre, un champ se remplit. Il faut donc une donnée qui évolue dans le temps et qui, quand elle change, met à jour l'affichage. C'est le state.
# Le state : la mémoire d'un composant
Une variable normale ne suffit pas : à chaque re-rendu, la fonction du composant est ré-exécutée, et la variable repart de zéro. Il faut une mémoire qui survit aux rendus. C'est le rôle du hook useState.
import { useState } from "react";
function Compteur() {
const [compte, setCompte] = useState(0);
return (
<div>
<span>Compte : {compte}</span>
<button onClick={() => setCompte(compte + 1)}>+1</button>
</div>
);
}import { useState } from "react"; function Compteur() { const [compte, setCompte] = useState(0); return ( <div> <span>Compte : {compte}</span> <button onClick={() => setCompte(compte + 1)}>+1</button> </div> );}useState(0) fait trois choses :
- il crée une variable d'état, initialisée à
0(la valeur passée) ; - il renvoie sa valeur actuelle (
compte) ; - et une fonction pour la modifier (
setCompte).
C'est exactement le compteur qu'on avait bricolé à la main en JavaScript... mais sans la fonction afficher() à rappeler soi-même.
# [valeur, setValeur] : une destructuration
useState renvoie un tableau de deux éléments, qu'on récupère par destructuration :
const [compte, setCompte] = useState(0);
// équivaut à :
// const resultat = useState(0);
// const compte = resultat[0];
// const setCompte = resultat[1];const [compte, setCompte] = useState(0);// équivaut à :// const resultat = useState(0);// const compte = resultat[0];// const setCompte = resultat[1];Par convention, on nomme le setter setXxx (compte → setCompte).
# Ce qui rend React « réactif » : le re-rendu
Le point clé : modifier le state via son setter déclenche un re-rendu. React ré-exécute la fonction du composant, recalcule le JSX, et met à jour le DOM - uniquement ce qui a changé.
Corollaire crucial : modifier une variable normale ne déclenche rien.
function Compteur() {
let compte = 0; // ❌ une variable normale
return (
<button onClick={() => {
compte++; // la variable change en mémoire...
console.log(compte); // ...mais l'affichage ne bouge JAMAIS
}}>
{compte}
</button>
);
}function Compteur() { let compte = 0; // ❌ une variable normale return ( <button onClick={() => { compte++; // la variable change en mémoire... console.log(compte); // ...mais l'affichage ne bouge JAMAIS }}> {compte} </button> );}Sans useState, React ne sait pas que quelque chose a changé. Le state est le signal qui lui dit « re-rends ».
# La règle d'or : on ne mute jamais le state
Pour les objets et les tableaux, il ne faut pas les modifier en place, mais en créer une nouvelle version. React détecte un changement en comparant les références : si tu mutes l'ancien tableau, sa référence ne change pas, et React ne re-rend pas.
const [taches, setTaches] = useState(["Coder"]);
// ❌ Mutation : même référence, React ne voit rien
taches.push("Tester");
setTaches(taches);
// ✅ Nouvelle référence via le spread
setTaches([...taches, "Tester"]);const [taches, setTaches] = useState(["Coder"]); // ❌ Mutation : même référence, React ne voit rientaches.push("Tester");setTaches(taches); // ✅ Nouvelle référence via le spreadsetTaches([...taches, "Tester"]);C'est le principe d'immutabilité vu côté JavaScript : on remplace, on ne modifie pas.
Quand le nouveau state dépend de l'ancien, préfère la forme fonction : setCompte(c => c + 1). React te passe la valeur la plus à jour, ce qui évite des bugs quand plusieurs mises à jour s'enchaînent.
# Qu'est-ce qu'un hook, au juste ?
Un hook est une fonction fournie par React dont le nom commence par use (useState, useEffect, useRef...). Elle permet de « se brancher » sur des fonctionnalités de React - l'état, le cycle de vie - depuis un composant.
Deux règles à respecter, sinon React s'y perd :
- Appeler les hooks au niveau racine du composant - jamais dans une condition, une boucle ou une fonction imbriquée.
- Uniquement depuis un composant (ou depuis un autre hook), pas depuis une fonction ordinaire.
// ❌ Interdit : hook dans une condition
if (connecte) {
const [nom, setNom] = useState("");
}
// ✅ Toujours au sommet du composant
const [nom, setNom] = useState("");// ❌ Interdit : hook dans une conditionif (connecte) { const [nom, setNom] = useState("");} // ✅ Toujours au sommet du composantconst [nom, setNom] = useState("");Pourquoi ? React associe chaque useState à un rendu par son ordre d'appel. Si l'ordre change d'un rendu à l'autre, il mélange les états. (C'est aussi pour ça qu'une closure capture le state du rendu où elle a été créée.)
# À retenir
- Le state est la mémoire d'un composant : une donnée qui survit aux rendus et déclenche une mise à jour quand elle change.
useStaterenvoie[valeur, setter]; c'est le setter qui provoque le re-rendu - muter une variable normale ne fait rien.- On ne mute jamais le state : on en crée une nouvelle version (spread), car React compare les références.
- Les hooks (
useXxx) s'appellent toujours au niveau racine d'un composant.
Prochaine étape : faire changer ce state et adapter l'affichage, avec Les événements et le rendu conditionnel.