React : état et effets
useState
Section intitulée « useState »import { useState } from 'react';
function Compteur() { const [n, setN] = useState(0); return ( <> <p>{n}</p> <button onClick={() => setN(n + 1)}>+1</button> <button onClick={() => setN((precedent) => precedent + 1)}> +1 (forme fonctionnelle) </button> </> );}- Modifier l’état uniquement par la fonction
set…; jamais par mutation directe. - Pour une valeur qui dépend de la précédente, utiliser la forme fonctionnelle (
setN(p => p + 1)). - Objets et tableaux : en créer de nouveaux (
{ ...ancien, champ: 1 },[...liste, element]).
const [utilisateur, setUtilisateur] = useState({ nom: 'Alice', age: 30 });setUtilisateur({ ...utilisateur, age: 31 });
const [taches, setTaches] = useState<string[]>([]);setTaches([...taches, 'Nouvelle tâche']);setTaches(taches.filter((t) => t !== 'Ancienne'));Placer l’état au bon endroit
Section intitulée « Placer l’état au bon endroit »- Dans le composant qui en a besoin, ou dans leur parent commun le plus proche (« remonter l’état »).
- Ne pas stocker ce qui peut se calculer :
const total = lignes.reduce(…)dans le rendu plutôt qu’un étattotal. - Ne pas dupliquer une donnée dans plusieurs états.
useReducer
Section intitulée « useReducer »Pour un état complexe ou plusieurs actions liées :
import { useReducer } from 'react';
type Action = { type: 'ajouter'; texte: string } | { type: 'supprimer'; id: number };type Tache = { id: number; texte: string };
function reducteur(etat: Tache[], action: Action): Tache[] { switch (action.type) { case 'ajouter': return [...etat, { id: Date.now(), texte: action.texte }]; case 'supprimer': return etat.filter((t) => t.id !== action.id); }}
function Taches() { const [taches, dispatch] = useReducer(reducteur, []); return ( <> <button onClick={() => dispatch({ type: 'ajouter', texte: 'À faire' })}> Ajouter </button> <ul> {taches.map((t) => ( <li key={t.id} onClick={() => dispatch({ type: 'supprimer', id: t.id })} > {t.texte} </li> ))} </ul> </> );}import { useRef } from 'react';
function Champ() { const entree = useRef<HTMLInputElement>(null); return ( <> <input ref={entree} /> <button onClick={() => entree.current?.focus()}>Focus</button> </> );}useRef conserve une valeur sans déclencher de rendu quand elle change : accès au DOM, identifiant de minuteur, valeur précédente.
useEffect
Section intitulée « useEffect »Un effet synchronise le composant avec un système extérieur (réseau, minuteur, abonnement, titre du document) après le rendu.
import { useEffect, useState } from 'react';
function Horloge() { const [heure, setHeure] = useState(new Date());
useEffect(() => { const id = setInterval(() => setHeure(new Date()), 1000); return () => clearInterval(id); // nettoyage }, []); // tableau de dépendances
return <p>{heure.toLocaleTimeString('fr-FR')}</p>;}| Tableau de dépendances | L’effet s’exécute |
|---|---|
| Absent | Après chaque rendu |
[] |
Une fois, au montage |
[a, b] |
Au montage, puis quand a ou b change |
Règles :
- Lister toutes les valeurs utilisées dans l’effet (le plugin ESLint
react-hooksle vérifie). - Nettoyer : annuler minuteurs, abonnements et requêtes dans la fonction renvoyée.
- Ne pas utiliser un effet pour calculer une valeur affichable ni pour réagir à un clic (placer cette logique dans le gestionnaire d’événement).
useEffect(() => { document.title = `${n} notifications`;}, [n]);En développement, StrictMode exécute chaque effet deux fois (montage, démontage, montage) pour détecter les nettoyages manquants.
Contexte
Section intitulée « Contexte »Partager une valeur avec toute une branche de l’arbre, sans la passer par chaque niveau :
import { createContext, useContext, useState, type ReactNode } from 'react';
type Theme = 'light' | 'dark';const ContexteTheme = createContext<{ theme: Theme; basculer: () => void;} | null>(null);
export function FournisseurTheme({ children }: { children: ReactNode }) { const [theme, setTheme] = useState<Theme>('light'); const basculer = () => setTheme((t) => (t === 'light' ? 'dark' : 'light')); return ( <ContexteTheme.Provider value={{ theme, basculer }}> {children} </ContexteTheme.Provider> );}
export function useTheme() { const contexte = useContext(ContexteTheme); if (!contexte) throw new Error('useTheme doit être utilisé dans FournisseurTheme'); return contexte;}Réserver le contexte aux données globales et peu changeantes (thème, utilisateur connecté, langue) : chaque changement de la valeur refait le rendu de tous les consommateurs.
Hooks personnalisés
Section intitulée « Hooks personnalisés »Réutiliser une logique d’état :
import { useEffect, useState } from 'react';
export function useStockageLocal<T>(cle: string, valeurInitiale: T) { const [valeur, setValeur] = useState<T>(() => { try { const enregistre = localStorage.getItem(cle); return enregistre ? (JSON.parse(enregistre) as T) : valeurInitiale; } catch { return valeurInitiale; } });
useEffect(() => { try { localStorage.setItem(cle, JSON.stringify(valeur)); } catch { /* stockage indisponible : ignoré */ } }, [cle, valeur]);
return [valeur, setValeur] as const;}Le nom d’un hook commence toujours par use. Les hooks s’appellent au niveau supérieur d’un composant ou d’un autre hook (jamais dans une condition, une boucle ou une fonction imbriquée).
Dépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
Too many re-renders |
set… appelé pendant le rendu : le placer dans un gestionnaire ou un effet |
Boucle infinie dans useEffect |
L’effet modifie une valeur de ses dépendances ; objet ou fonction recréés à chaque rendu |
| L’état ne se met pas à jour comme prévu | Valeur lue avant la mise à jour : forme fonctionnelle de set… |
Mutation directe sans rendu (liste.push(x)) |
Créer un nouveau tableau |
Rendered more hooks than during the previous render |
Hook appelé dans une condition |
| Effet exécuté deux fois en développement | StrictMode : ajouter le nettoyage |
Can't perform a React state update on an unmounted component |
Requête ou minuteur non annulé : nettoyage |
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »Aucune.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Gérer l’état, Effets
- Suite : Formulaires et événements