> Guide complet des Hooks React : useState, useEffect (dépendances, cleanup), useRef, useMemo, useCallback, useContext, règles des hooks, hook personnalisé et pièges courants.

*Source : https://coder-studio.com/tutoriels/hooks-react/*

---

[// Web](https://coder-studio.com/tutoriels/)

# Les Hooks React : useState, useEffect et les autres

Par **Kévin Papot** · publié le 24 janvier 2026

Les **Hooks** ont transformé la façon d’écrire React. Introduits pour permettre aux composants fonctions de gérer un état et des effets, ils ont rendu les classes largement obsolètes. Un hook est simplement une fonction spéciale, préfixée par `use`, qui « accroche » votre composant à des fonctionnalités de React. Ce guide passe en revue les hooks essentiels, leurs pièges, et la création de vos propres hooks.

## `useState` : gérer l’état local

`useState` est le hook fondateur. Il ajoute une variable d’état à un composant fonction et déclenche un nouveau rendu à chaque modification.

```
import { useState } from "react";

function Compteur() {
  const [compte, setCompte] = useState(0);

  return (
    <button onClick={() => setCompte(compte + 1)}>
      Cliqué {compte} fois
    </button>
  );
}
```

`useState` renvoie un tableau de deux éléments : la valeur actuelle et une fonction pour la modifier. On les récupère par déstructuration. Point crucial : ne modifiez **jamais** l’état directement (`compte++`), utilisez toujours la fonction de mise à jour, sinon React ne détecte pas le changement.

Quand la nouvelle valeur dépend de l’ancienne, préférez la **forme fonctionnelle** pour éviter les bugs de valeur périmée :

```
// ✅ Fiable : React fournit la valeur la plus à jour
setCompte((prev) => prev + 1);

// ⚠️ Risqué si plusieurs mises à jour s'enchaînent
setCompte(compte + 1);
```

Si les bases de React ne sont pas encore acquises, notre [guide React pour débuter](https://coder-studio.com/tutoriels/react-guide-debutant/) présente les composants et le JSX avant d’attaquer les hooks.

## `useEffect` : gérer les effets de bord

`useEffect` sert à synchroniser votre composant avec l’extérieur : appels réseau, abonnements, minuteurs, manipulation directe du DOM. Il s’exécute **après** le rendu.

```
import { useState, useEffect } from "react";

function Profil({ userId }: { userId: number }) {
  const [nom, setNom] = useState<string | null>(null);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then((r) => r.json())
      .then((data) => setNom(data.nom));
  }, [userId]); // relancé uniquement quand userId change

  return <p>{nom ?? "Chargement..."}</p>;
}
```

### Le tableau de dépendances

Le second argument, le **tableau de dépendances**, contrôle _quand_ l’effet se relance. Trois cas de figure :

```
useEffect(() => { /* ... */ });        // à CHAQUE rendu (rarement voulu)
useEffect(() => { /* ... */ }, []);    // une SEULE fois, au montage
useEffect(() => { /* ... */ }, [a, b]); // quand a OU b change
```

La règle d’or : **toute valeur utilisée dans l’effet et issue du composant doit figurer dans les dépendances**. Oublier une dépendance est la première source de bugs avec `useEffect` (valeurs figées, données non rafraîchies).

### Le nettoyage (cleanup)

Si votre effet crée quelque chose qui doit être défait (un abonnement, un minuteur, un écouteur d’événement), retournez une **fonction de nettoyage**. React l’appelle avant de relancer l’effet et au démontage du composant.

```
useEffect(() => {
  const id = setInterval(() => console.log("tic"), 1000);

  // Nettoyage : indispensable, sinon le minuteur fuit
  return () => clearInterval(id);
}, []);
```

Sans ce nettoyage, vous accumulez des minuteurs ou des abonnements fantômes qui provoquent fuites mémoire et comportements imprévisibles.

## `useRef` : une valeur persistante sans re-rendu

`useRef` conserve une valeur mutable qui **survit aux rendus sans en déclencher**. Ses deux usages principaux : accéder à un élément du DOM, et stocker une valeur « hors état ».

```
import { useRef, useEffect } from "react";

function ChampAutoFocus() {
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    inputRef.current?.focus(); // accès direct au DOM
  }, []);

  return <input ref={inputRef} placeholder="Focus automatique" />;
}
```

Contrairement à `useState`, modifier `ref.current` ne relance **pas** le rendu. C’est exactement ce qu’on veut pour garder l’identifiant d’un minuteur ou la valeur précédente d’une prop, sans provoquer d’affichage inutile.

## `useMemo` et `useCallback` : optimiser les recalculs

Ces deux hooks servent à la **mémoïsation** : éviter de recalculer ou recréer quelque chose à chaque rendu. `useMemo` mémorise une **valeur** ; `useCallback` mémorise une **fonction**.

```
import { useMemo, useCallback } from "react";

function Liste({ items, filtre }: { items: string[]; filtre: string }) {
  // useMemo : ne refiltre que si items ou filtre changent
  const visibles = useMemo(
    () => items.filter((i) => i.includes(filtre)),
    [items, filtre]
  );

  // useCallback : garde la même référence de fonction entre rendus
  const onClick = useCallback((item: string) => {
    console.log("clic sur", item);
  }, []);

  return (
    <ul>
      {visibles.map((i) => (
        <li key={i} onClick={() => onClick(i)}>{i}</li>
      ))}
    </ul>
  );
}
```

`useCallback(fn, deps)` est en réalité un raccourci pour `useMemo(() => fn, deps)`. On l’utilise surtout pour passer une fonction stable à un composant enfant optimisé, afin d’éviter des re-rendus inutiles. Attention cependant : **n’optimisez pas prématurément**. Ces hooks ont un coût, et sur des calculs légers ils n’apportent rien, voire dégradent la lisibilité.

## `useContext` : partager des données sans props

`useContext` lit une valeur fournie par un `Context`, ce qui évite de faire descendre une prop à travers de nombreux niveaux (le fameux _prop drilling_). Idéal pour un thème, une langue ou l’utilisateur connecté.

```
import { createContext, useContext } from "react";

const ThemeContext = createContext<"clair" | "sombre">("clair");

function Bouton() {
  const theme = useContext(ThemeContext); // lit la valeur la plus proche
  return <button className={theme}>Valider</button>;
}

function App() {
  return (
    <ThemeContext.Provider value="sombre">
      <Bouton />
    </ThemeContext.Provider>
  );
}
```

## Les règles des hooks

Deux règles impératives, vérifiées par l’outillage React, garantissent le bon fonctionnement des hooks :

1.  **Appelez les hooks uniquement au niveau supérieur.** Jamais dans une condition, une boucle ou une fonction imbriquée. React s’appuie sur l’**ordre d’appel** pour associer chaque hook à son état ; un appel conditionnel casse cet ordre.
2.  **Appelez les hooks uniquement depuis un composant React ou un autre hook.** Pas depuis une fonction JavaScript ordinaire.

```
function Mauvais({ actif }: { actif: boolean }) {
  if (actif) {
    const [x, setX] = useState(0); // ❌ hook dans une condition
  }
}

function Bon({ actif }: { actif: boolean }) {
  const [x, setX] = useState(0);   // ✅ toujours appelé, au sommet
  if (actif) { /* utilise x ici */ }
}
```

## Créer un hook personnalisé

Dès qu’une logique à base de hooks se répète, extrayez-la dans un **hook personnalisé** : une fonction dont le nom commence par `use` et qui appelle d’autres hooks. C’est le mécanisme de réutilisation le plus élégant de React.

```
import { useState, useEffect } from "react";

// Hook réutilisable : lire/écrire dans le localStorage
function useLocalStorage<T>(cle: string, initial: T) {
  const [valeur, setValeur] = useState<T>(() => {
    const brut = localStorage.getItem(cle);
    return brut ? (JSON.parse(brut) as T) : initial;
  });

  useEffect(() => {
    localStorage.setItem(cle, JSON.stringify(valeur));
  }, [cle, valeur]);

  return [valeur, setValeur] as const;
}

// Utilisation, aussi simple que useState
function Preferences() {
  const [langue, setLangue] = useLocalStorage("langue", "fr");
  return (
    <select value={langue} onChange={(e) => setLangue(e.target.value)}>
      <option value="fr">Français</option>
      <option value="en">English</option>
    </select>
  );
}
```

Le hook encapsule toute la logique de synchronisation ; le composant, lui, reste limpide. C’est ainsi qu’on partage du comportement stateful entre composants sans dupliquer de code.

## Les pièges les plus fréquents

-   **Dépendances manquantes dans `useEffect`** → valeurs figées et données non rafraîchies. Complétez le tableau, ou repensez l’effet.
-   **Oublier le nettoyage** → fuites mémoire (minuteurs, abonnements accumulés).
-   **Muter l’état directement** (`items.push(...)`) → React ne détecte pas le changement. Créez une nouvelle référence (`setItems([...items, x])`).
-   **Sur-optimiser avec `useMemo`/`useCallback`** → complexité inutile pour un gain nul sur des calculs légers.
-   **Appeler un hook conditionnellement** → viole les règles des hooks et casse l’état.

## En résumé

Les hooks donnent aux composants fonctions toute la puissance de React : état avec `useState`, effets de bord avec `useEffect`, références avec `useRef`, optimisation avec `useMemo`/`useCallback`, et partage de données avec `useContext`. Respectez les deux règles fondamentales, soignez vos tableaux de dépendances, et extrayez vos logiques récurrentes dans des hooks personnalisés. Vous tenez là l’essentiel du React moderne.

## À lire ensuite

-   [React : le guide pour bien débuter](https://coder-studio.com/tutoriels/react-guide-debutant/)
-   [Angular vs React : le comparatif pour choisir](https://coder-studio.com/tutoriels/angular-vs-react/)
-   [Les generics en TypeScript : le guide](https://coder-studio.com/tutoriels/generics-typescript/)
