SmarMaker - Documentation
Docs» 15_training:module3-hooks-fondamentaux:useeffect

Chapitre 2 : useEffect

Ce chapitre est **crucial**. useEffect est le hook le plus complexe à maîtriser, mais aussi le plus utilisé après useState.

Qu'est-ce qu'un effet de bord ?

Un effet de bord (side effect) est toute opération qui affecte quelque chose en dehors du composant :

  • Appeler une API
  • Modifier le titre de la page
  • S'abonner à un événement (WebSocket, resize, scroll)
  • Manipuler le DOM directement
  • Utiliser un timer (setTimeout, setInterval)

Ces opérations ne peuvent pas être faites directement dans le corps du composant, car celui-ci est exécuté à chaque rendu.

Syntaxe de base

snippet.javascript
import { useEffect } from 'react';
 
function MyComponent() {
    useEffect(() => {
        // Code exécuté après le rendu
        console.log('Composant rendu !');
    });
 
    return <div>Mon composant</div>;
}

Le tableau de dépendances

Le deuxième argument de useEffect contrôle quand l'effet s'exécute :

Sans tableau : à chaque rendu

snippet.javascript
useEffect(() => {
    console.log('Exécuté après CHAQUE rendu');
});

Rarement utile - peut causer des problèmes de performance.

Tableau vide : une seule fois

snippet.javascript
useEffect(() => {
    console.log('Exécuté UNE SEULE FOIS après le premier rendu');
}, []);

Équivalent à componentDidMount en classes.

Avec dépendances : quand elles changent

snippet.javascript
useEffect(() => {
    console.log('Exécuté quand userId change');
    fetchUser(userId);
}, [userId]);

L'effet s'exécute :

  1. Après le premier rendu
  2. Après chaque rendu où userId a changé

Exemple concret : appel API

snippet.javascript
function UserProfile({ userId }) {
    const [user, setUser] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);
 
    useEffect(() => {
        // Fonction async dans useEffect
        const fetchUser = async () => {
            setLoading(true);
            setError(null);
 
            try {
                const response = await fetch(`/api/users/${userId}`);
                const data = await response.json();
                setUser(data);
            } catch (err) {
                setError(err.message);
            } finally {
                setLoading(false);
            }
        };
 
        fetchUser();
    }, [userId]);  // Re-fetch quand userId change
 
    if (loading) return <p>Chargement...</p>;
    if (error) return <p>Erreur : {error}</p>;
    if (!user) return null;
 
    return <div>{user.name}</div>;
}

Fonction de nettoyage (cleanup)

useEffect peut retourner une fonction qui sera appelée :

  • Avant la prochaine exécution de l'effet
  • Quand le composant est démonté
snippet.javascript
useEffect(() => {
    // Setup
    const subscription = api.subscribe(data => {
        setData(data);
    });
 
    // Cleanup
    return () => {
        subscription.unsubscribe();
    };
}, []);

Exemple : événement de redimensionnement

snippet.javascript
function WindowSize() {
    const [size, setSize] = useState({
        width: window.innerWidth,
        height: window.innerHeight
    });
 
    useEffect(() => {
        const handleResize = () => {
            setSize({
                width: window.innerWidth,
                height: window.innerHeight
            });
        };
 
        window.addEventListener('resize', handleResize);
 
        // Cleanup : retirer l'écouteur
        return () => {
            window.removeEventListener('resize', handleResize);
        };
    }, []);  // [] car on veut s'abonner une seule fois
 
    return <p>{size.width} x {size.height}</p>;
}

Exemple : timer

snippet.javascript
function Timer() {
    const [seconds, setSeconds] = useState(0);
 
    useEffect(() => {
        const interval = setInterval(() => {
            setSeconds(s => s + 1);
        }, 1000);
 
        // Cleanup : arrêter le timer
        return () => {
            clearInterval(interval);
        };
    }, []);
 
    return <p>{seconds} secondes</p>;
}

Pièges courants

1. Boucle infinie

snippet.javascript
// DANGER ! Boucle infinie
useEffect(() => {
    setCount(count + 1);  // Modifie l'état
});  // Pas de tableau = exécuté à chaque rendu
// Modifie l'état → re-rendu → useEffect → modifie l'état → ...

Solution : toujours mettre un tableau de dépendances.

2. Dépendances manquantes

snippet.javascript
// PROBLÈME : userId n'est pas dans les dépendances
useEffect(() => {
    fetchUser(userId);
}, []);  // Fetch uniquement au montage, ignore les changements de userId

Solution : ajouter toutes les valeurs utilisées dans le tableau.

snippet.javascript
useEffect(() => {
    fetchUser(userId);
}, [userId]);  // Correct

3. Objet ou fonction dans les dépendances

snippet.javascript
// PROBLÈME : l'objet est recréé à chaque rendu
function MyComponent({ user }) {
    const options = { limit: 10 };  // Nouvel objet à chaque rendu
 
    useEffect(() => {
        fetchData(options);
    }, [options]);  // options change à chaque rendu !
}

Solution : déplacer hors du composant ou utiliser useMemo (module suivant).

4. Async dans useEffect

snippet.javascript
// INCORRECT - useEffect ne peut pas être async
useEffect(async () => {  // Erreur !
    const data = await fetchData();
}, []);
 
// CORRECT - définir une fonction async à l'intérieur
useEffect(() => {
    const loadData = async () => {
        const data = await fetchData();
        setData(data);
    };
    loadData();
}, []);

Ordre d'exécution

snippet.javascript
function MyComponent() {
    console.log('1. Rendu');
 
    useEffect(() => {
        console.log('3. Effet');
        return () => console.log('2. Cleanup (si re-rendu)');
    });
 
    return <div>Test</div>;
}
 
// Premier rendu :
// 1. Rendu
// 3. Effet
 
// Re-rendu :
// 1. Rendu
// 2. Cleanup
// 3. Effet

Comparaison avec le cycle de vie PHP

Événement PHP React useEffect
Initialisation constructeur useEffect(() => {}, [])
À chaque requête code du contrôleur useEffect(() => {})
Nettoyage destructeur fonction retournée par useEffect

Quand utiliser useEffect ?

OUI :

  • Appels API
  • Abonnements (WebSocket, events)
  • Timers
  • Modifier le DOM directement
  • Synchroniser avec un système externe

NON :

  • Calculer une valeur dérivée de l'état → useMemo
  • Transformer des données pour le rendu → faire dans le rendu
  • Réagir à une action utilisateur → gestionnaire d'événement

Exercices

Exercice 1 : Titre de page dynamique

Créer un composant qui met à jour le titre de la page avec le compteur.

Solution :

snippet.javascript
function Counter() {
    const [count, setCount] = useState(0);
 
    useEffect(() => {
        document.title = `Compteur : ${count}`;
    }, [count]);
 
    return (
        <div>
            <p>{count}</p>
            <button onClick={() => setCount(c => c + 1)}>+1</button>
        </div>
    );
}

Exercice 2 : Fetch avec gestion d'erreur

Créer un composant qui charge une liste de posts depuis une API.

Solution :

snippet.javascript
function PostList() {
    const [posts, setPosts] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);
 
    useEffect(() => {
        const fetchPosts = async () => {
            try {
                const response = await fetch('https://jsonplaceholder.typicode.com/posts');
                if (!response.ok) throw new Error('Erreur réseau');
                const data = await response.json();
                setPosts(data.slice(0, 10));
            } catch (err) {
                setError(err.message);
            } finally {
                setLoading(false);
            }
        };
 
        fetchPosts();
    }, []);
 
    if (loading) return <p>Chargement...</p>;
    if (error) return <p>Erreur : {error}</p>;
 
    return (
        <ul>
            {posts.map(post => (
                <li key={post.id}>{post.title}</li>
            ))}
        </ul>
    );
}

Points clés à retenir

  1. useEffect exécute du code après le rendu
  2. Tableau vide [] = une seule fois au montage
  3. Avec dépendances = quand ces valeurs changent
  4. Fonction de retour = nettoyage (cleanup)
  5. Toujours lister les dépendances utilisées dans l'effet
  6. Async : définir une fonction async à l'intérieur, pas sur useEffect

← Chapitre précédent | Retour au module | Chapitre suivant : useRef →

Previous Next

Made with ❤ by CAP-REL · SmartMaker · GNU AGPL v3+
Code source · Faire un don
SmarMaker - Documentation
Traductions de cette page:
  • Français
  • Deutsch
  • English
  • Español
  • Italiano
  • Nederlands

Table of Contents

Table des matières

  • Chapitre 2 : useEffect
    • Qu'est-ce qu'un effet de bord ?
    • Syntaxe de base
    • Le tableau de dépendances
      • Sans tableau : à chaque rendu
      • Tableau vide : une seule fois
      • Avec dépendances : quand elles changent
    • Exemple concret : appel API
    • Fonction de nettoyage (cleanup)
      • Exemple : événement de redimensionnement
      • Exemple : timer
    • Pièges courants
      • 1. Boucle infinie
      • 2. Dépendances manquantes
      • 3. Objet ou fonction dans les dépendances
      • 4. Async dans useEffect
    • Ordre d'exécution
    • Comparaison avec le cycle de vie PHP
    • Quand utiliser useEffect ?
    • Exercices
      • Exercice 1 : Titre de page dynamique
      • Exercice 2 : Fetch avec gestion d'erreur
    • Points clés à retenir
  • SmartAuth
  • SmartMaker - Back (PHP)
    • Mapping Dolibarr - React
  • SmartMaker - Front (React)
    • Animations de pages
    • Architecture
    • Astuces
    • Calendar
    • Composants et pages
    • Configuration du Provider
    • Debug et Logs
    • Hooks SmartCommon
    • PWA (Progressive Web App)
    • Requêtes API
    • Routage
    • SmartCommon
    • Stockage de données
    • Thèmes
    • Traductions
  • HowTo - Pas à pas - Votre première application
    • Développement PHP (back)
    • Développement React (front)
    • Première étape : Module Builder Dolibarr
    • SmartAuth
    • SmartBoot : Un "squelette" quasiment prêt à l'emploi
  • Formation SmartMaker
    • Module 1 : Fondamentaux JavaScript ES6+
      • Chapitre 1 : Variables et Scope
      • Chapitre 2 : Fonctions
      • Chapitre 3 : Programmation Asynchrone
      • Chapitre 4 : Modules ES6
    • Module 2 : Introduction à React
      • Chapitre 1 : Philosophie React
      • Chapitre 2 : JSX
      • Chapitre 3 : Composants
    • Module 3 : Hooks React Fondamentaux
      • Chapitre 1 : useState
      • Chapitre 2 : useEffect
      • Chapitre 3 : useRef
      • Chapitre 4 : useContext
    • Module 4 : React Avancé
      • Chapitre 1 : useCallback et useMemo
      • Chapitre 2 : Custom Hooks
      • Chapitre 3 : Redux et Redux Toolkit
    • Module 5 : Architecture SmartMaker
      • Chapitre 1 : Structure du projet
      • Chapitre 2 : Configuration
      • Chapitre 3 : Flux de données
    • Module 6 : SmartCommon - Composants
      • Chapitre 1 : Mise en page
      • Chapitre 2 : Navigation
      • Chapitre 3 : Formulaires
      • Chapitre 4 : Affichage
    • Module 7 : SmartCommon - Hooks
      • Chapitre 1 : useApi
      • Chapitre 2 : Gestion d'état
      • Chapitre 3 : Hooks utilitaires
    • Module 8 : Backend API (PHP)
      • Chapitre 1 : Routage
      • Chapitre 2 : Controllers
      • Chapitre 3 : Mappers
      • Extrafields et formulaires dynamiques
    • Module 9 : Intégration complète
      • Chapitre 1 : Backend
      • Chapitre 2 : Frontend
      • Chapitre 3 : Déploiement
    • Module 10 : Fonctionnalités avancées
      • Chapitre 1 : Mode offline
      • Chapitre 2 : Internationalisation (i18n)
      • Chapitre 3 : Autres fonctionnalités
    • Module 11 : Bonnes pratiques
  • Démonstration
  • Start
  • Composants et pages
  • Afficher le texte source
  • Anciennes révisions
  • Liens de retour
  • Haut de page