Aller au contenu

Réussir ses Effets Secondaires en React avec useEffect

Introduction

En explorant React, j’ai souvent utilisé useEffect sans trop y réfléchir. Lors d’une formation, j’ai découvert que certaines de mes habitudes n’étaient pas optimales et pouvaient même poser problème.

Dans cet article, je partage avec vous l’essentiel sur useEffect : comment l’utiliser correctement, éviter les erreurs courantes et adopter les meilleures pratiques.

UseEffect c’est quoi ?

useEffect est un hook puissant dans React, conçu pour gérer les effets secondaires dans les composants fonctionnels. Il remplace les méthodes de cycle de vie telles que componentDidMount, componentDidUpdate, et componentWillUnmount.

Syntaxe et Utilisation

La syntaxe de base de useEffect est la suivante :

useEffect(() => {
  // code à exécuter lorsque l'effet est déclenché
}, [dependances]);

Il prend deux arguments : une fonction callback et un tableau de dépendances optionnel. La fonction de rappel contient le code à exécuter, tandis que le tableau de dépendances spécifie les valeurs à surveiller pour les changements.

Les dépendances jouent un rôle essentiel dans le contrôle de l’exécution de useEffect.

useEffect(() => {
}, []);

L’utilisation d’un useEffect avec un tableau vide comme dépendances, fait que l’effet sera lancé une seule fois, lors du premier rendu du composant, un peu comme avec componentDidMount

useEffect(() => {
  // code à exécuter lorsque l'effet est déclenché
});

Si l’on omet le tableau de dépendances, l’effet sera exécuté à chaque rendu, ce qui peut nuire aux performances.

useEffect(() => {
}, [valeur]);

Ici, l’effet sera exécuté chaque fois que la valeur spécifiée change simulant componentDidUpdate.

Nettoyage des Effets Secondaires

Un autre aspect important de useEffect est le nettoyage des effets secondaires. Cela permet d’éviter des fuites de mémoire, notamment lorsque le composant est démonté ou que l’effet doit être réexécuté.

useEffect(() => {
    const interval = setInterval(() => {
        // Perform some repeated action
    }, 1000);
    return () => {
        clearInterval(interval); // Clean up the interval
    };
}, []);

Erreurs Courantes et Meilleures Pratiques avec useEffect

L’utilisation de useEffect peut rapidement devenir source d’erreurs si certaines précautions ne sont pas prises. Voici les erreurs les plus fréquentes et comment les éviter en appliquant les bonnes pratiques.

1. Oublier le tableau de dépendances

Erreur :

useEffect(() => {
  fetchData();
});

Problème : L’effet s’exécute à chaque rendu, entraînant des requêtes répétées et une surcharge inutile.

Bonne pratique : Toujours spécifier un tableau de dépendances, même s’il est vide ([]).

2. Mauvaise gestion des dépendances

Erreur :

useEffect(() => {
console.log(count);
}, []);

Problème : L’effet ne se déclenche pas lorsque count change, car il n’est pas inclus dans les dépendances.

Bonne pratique : Inclure toutes les variables utilisées dans le useEffect dans le tableau de dépendances.

3. Créer une boucle infinie

Erreur :

useEffect(() => {
  setState(state + 1);
}, [state]);

Problème : La modification de state entraîne un nouveau rendu, ce qui relance l’effet en boucle infinie.

Bonne pratique : Utiliser une mise à jour basée sur l’état précédent pour éviter un déclenchement infini.

useEffect(() => {
  setState(prevState => prevState + 1);
}, []);

4. Oublier le nettoyage d’un effet

Erreur :

useEffect(() => {
  window.addEventListener("resize", handleResize);
}, []);

Problème : L’écouteur d’événement continue d’exister même après le démontage du composant, ce qui entraîne des fuites de mémoire.

Bonne pratique : Toujours retourner une fonction de nettoyage (cleanup function). Cette approche est particulièrement utile pour les abonnements (WebSocket, setInterval, etc.).

useEffect(() => {
  const handleResize = () => console.log("Fenêtre redimensionnée !");
  window.addEventListener("resize", handleResize);

  return () => {
    window.removeEventListener("resize", handleResize);
  };
}, []);

5. Ne pas abuser de useEffect

Mauvaise habitude : Utiliser useEffect pour tout, même quand ce n’est pas nécessaire.
Parfois, un effet peut être remplacé par :

  • useMemo pour mémoriser le résultat d’un calcul coûteux ou dépendant de certaines données.
  • useCallback pour mémoriser des fonctions.
  • Lever l’effet en dehors du composant si possible (ex. : via un contexte global).
  • useReducer pour gérer un état complexe ou conditionnel à partir de plusieurs actions. Contrairement à useState, il centralise la logique de mise à jour, ce qui évite d’avoir plusieurs useEffect en cascade pour synchroniser les états.

Exemple :
Mauvais usage de useEffect pour dériver un état :

useEffect(() => {
  setFilteredList(list.filter(item => item.active));
}, [list]);

Meilleure alternative avec useMemo :

useMemo permet de recalculer filteredList uniquement quand list change, sans effet secondaire :

const filteredList = useMemo(() => list.filter(item => item.active), [list]);

C’est plus simple, plus lisible, sans gestion manuelle de setState ni dépendance au cycle de vie du composant.

un autre exemple d’utilisation d’useMemo :

function UserList({ users }) {
  const activeUsers = useMemo(() => {
    return users.filter(user => user.isActive);
  }, [users]);

  return (
    <ul>
      {activeUsers.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

Ici, le tableau users peut être long ou mis à jour fréquemment. Grâce à useMemo, on évite de recalculer activeUsers à chaque rendu si users n’a pas changé.

Conclusion

useEffect est un outil puissant mais qui peut vite causer des soucis s’il est mal utilisé. En gérant bien les dépendances, en évitant les re-rendus inutiles et en nettoyant correctement ses effets, on améliore la performance et la stabilité des composants.