⚡ Qu'est-ce que le state ?
Le state (état) est la mémoire locale d'un composant — il représente les données qui peuvent changer au fil du temps et qui déclenchent un re-rendu quand elles sont mises à jour.
- Props — données passées depuis le parent (en lecture seule)
- State — données locales qui peuvent être modifiées par le composant lui-même
- Quand le state change, React re-render le composant automatiquement
count = count + 1 ne déclenche pas de re-render ! Toujours passer par le setter : setCount(count + 1).📝 Syntaxe — useState
useState retourne un tableau avec deux éléments : la valeur actuelle et la fonction pour la modifier.
import { useState } from 'react';
function Compteur() {
// [valeurActuelle, fonctionMiseAJour] = useState(valeurInitiale)
const [count, setCount] = useState(0);
const [nom, setNom] = useState('');
const [actif, setActif] = useState(false);
return (
<div>
<p>Compteur : {count}</p>
<button onClick={() => setCount(count + 1)}>Incrémenter</button>
<input value={nom} onChange={e => setNom(e.target.value)} />
<p>Bonjour {nom}</p>
<button onClick={() => setActif(!actif)}>
{actif ? 'Désactiver' : 'Activer'}
</button>
</div>
);
}
🔄 Mise à jour du state
Deux façons de mettre à jour : valeur directe ou fonction updater (recommandée quand la nouvelle valeur dépend de l'ancienne).
function Compteur() {
const [count, setCount] = useState(0);
// ❌ Problème : stale closure — peut lire une ancienne valeur
function incrementerDirect() {
setCount(count + 1);
setCount(count + 1); // compte toujours comme 1, pas 2 !
}
// ✅ Correct : fonction updater — toujours basée sur la dernière valeur
function incrementerCorrect() {
setCount(prev => prev + 1);
setCount(prev => prev + 1); // compte correctement +2
}
// Mise à jour conditionnelle
function incrementerSiInferieur(max) {
setCount(prev => prev < max ? prev + 1 : prev);
}
return (
<div>
<p>{count}</p>
<button onClick={incrementerCorrect}>+1</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
setCount(prev => prev + 1) dès que la nouvelle valeur dépend de l'ancienne — c'est toujours plus sûr.📦 State avec objets et tableaux
Le state en React doit être traité comme immutable — ne jamais muter directement, toujours créer une nouvelle copie.
// State objet — toujours créer un nouvel objet
const [utilisateur, setUtilisateur] = useState({ nom: '', age: 0 });
// ❌ Mutation directe — ne PAS faire ça
utilisateur.nom = 'Alice'; // React ne le détecte pas
// ✅ Spread operator — crée un nouvel objet
setUtilisateur({ ...utilisateur, nom: 'Alice' });
// State tableau — toujours créer un nouveau tableau
const [items, setItems] = useState([]);
// Ajouter
setItems(prev => [...prev, nouvelItem]);
// Supprimer
setItems(prev => prev.filter(item => item.id !== idASupprimer));
// Modifier
setItems(prev => prev.map(item =>
item.id === idACibler ? { ...item, done: true } : item
));
🧮 State dérivé
Ne pas stocker dans le state ce qui peut être calculé depuis le state existant — utiliser des variables simples à la place.
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, texte: 'Apprendre React', done: true },
{ id: 2, texte: 'Créer un projet', done: false },
]);
// ✅ State dérivé — calculé depuis todos (pas besoin de useState)
const total = todos.length;
const termines = todos.filter(t => t.done).length;
const progression = total > 0 ? Math.round(termines / total * 100) : 0;
return (
<div>
<p>{termines}/{total} — {progression}%</p>
{/* liste des todos */}
</div>
);
}
⬆️ Remonter le state (Lifting State Up)
Quand plusieurs composants doivent partager du state, on le déplace vers leur ancêtre commun le plus proche.
// ✅ Le state est dans le parent commun
function App() {
const [temperature, setTemperature] = useState(22);
return (
<div>
{/* Les deux composants partagent le même state */}
<Celsius temp={temperature} onChange={setTemperature} />
<Fahrenheit temp={temperature * 9/5 + 32} />
</div>
);
}
function Celsius({ temp, onChange }) {
return (
<input
type="number"
value={temp}
onChange={e => onChange(Number(e.target.value))}
/>
);
}
function Fahrenheit({ temp }) {
return <p>{temp.toFixed(1)} °F</p>;
}