09 — useEffect

🔍 C'est quoi un « effet de bord » ?

Un composant React doit être pur : il calcule du JSX à partir de ses props/state. Tout le reste est un effet de bord :

useEffect permet d'exécuter ces effets après le rendu.

📐 Syntaxe de base

const { useEffect, useState } = React;

useEffect(() => {
    // Code exécuté après le rendu
    console.log('Effet exécuté !');
});

🔄 Les 3 formes de useEffect

Syntaxe Quand l'effet s'exécute
useEffect(() => { ... }) Après chaque rendu (rarement souhaité)
useEffect(() => { ... }, []) Uniquement au montage (1 seule fois)
useEffect(() => { ... }, [a, b]) Au montage + quand a ou b change

1. Sans dépendances — à chaque rendu

useEffect(() => {
    console.log('Rendu effectué');
});
// ⚠️ S'exécute à CHAQUE rendu — rarement ce qu'on veut

2. Tableau vide [] — au montage uniquement

useEffect(() => {
    console.log('Composant monté !');
    // Parfait pour : fetch initial, setup
}, []);
// Ne s'exécute qu'UNE seule fois

3. Avec dépendances [dep1, dep2]

useEffect(() => {
    document.title = `Compteur : ${count}`;
}, [count]);
// S'exécute au montage + quand "count" change

🧹 La fonction de cleanup

L'effet peut retourner une fonction de nettoyage, appelée quand le composant se démonte ou avant la ré-exécution de l'effet :

useEffect(() => {
    // Setup
    const timer = setInterval(() => {
        console.log('tick');
    }, 1000);

    // Cleanup — appelé au démontage
    return () => {
        clearInterval(timer);
        console.log('Timer nettoyé');
    };
}, []);

Cycle de vie avec useEffect :

Montage (1er rendu)
  → Effet exécuté (setup)

Mise à jour (re-rendu si dépendance change)
  → Cleanup de l'ancien effet
  → Nouvel effet exécuté

Démontage (composant retiré)
  → Cleanup final

📡 Exemple : Appel API avec fetch

function ListeUsers() {
    const [users, setUsers] = useState([]);
    const [loading, setLoading] = useState(true);
    const [erreur, setErreur] = useState(null);

    useEffect(() => {
        fetch('https://jsonplaceholder.typicode.com/users')
            .then(res => {
                if (!res.ok) throw new Error('Erreur réseau');
                return res.json();
            })
            .then(data => {
                setUsers(data);
                setLoading(false);
            })
            .catch(err => {
                setErreur(err.message);
                setLoading(false);
            });
    }, []); // [] = une seule fois au montage

    if (loading) return <p>Chargement...</p>;
    if (erreur) return <p>Erreur : {erreur}</p>;

    return (
        <ul>
            {users.map(u => <li key={u.id}>{u.name}</li>)}
        </ul>
    );
}
⚠️ Piège classique : Ne pas mettre d'async directement sur le callback de useEffect :
// ❌ INTERDIT
useEffect(async () => { ... }, []);

// ✅ Créer une fonction async à l'intérieur
useEffect(() => {
    const fetchData = async () => {
        const res = await fetch(url);
        const data = await res.json();
        setData(data);
    };
    fetchData();
}, []);

🚀 Démo interactive

▼ useEffect en action : timer, titre, fetch

📝 Résumé

← 08 — Listes et clés 10 — Formulaires →