React veut que vos composants soient prévisibles. Donnez-lui le même état et les mêmes props, et il devrait afficher la même interface utilisateur à chaque fois. Mais la plupart des applications réelles ne peuvent pas survivre à l'intérieur de cette bulle. Elles doivent interagir avec l'extérieur. Un tableau de bord a besoin de chiffres frais provenant d'un serveur. Un widget de chat doit écouter les messages. Un minuteur doit s'incrémenter. Ces opérations sont des effets de bord (side effects), et elles se situent en dehors du cycle de rendu de React. Le hook useEffect est l'endroit où vous placez ce travail désordonné et imprévisible afin que votre composant lui-même reste cohérent.
Effets de bord : ce qui doit aller dans useEffect
Un effet de bord est tout ce qui touche au monde extérieur au-delà du retour de JSX. La phase de rendu de React doit être pure. Lorsque vous commencez à récupérer des données, à écrire dans des variables globales ou à attacher des écouteurs au DOM, vous sortez du territoire de la pureté.
Les exemples courants incluent :
- La récupération de données depuis une API
- La mise en place de minuteurs ou d'intervalles
- L'ajout d'écouteurs d'événements à
windowoudocument - La mise à jour du titre de l'onglet du navigateur
- La connexion à des WebSockets
Ces tâches partagent un point commun : elles n'ont pas leur place dans l'instruction return de votre composant ou dans la logique de rendu principale. Tenter d'appeler une API du navigateur comme setInterval directement à l'intérieur du corps du rendu déclenchera l'appel à chaque rendu, créant des minuteurs en double et un comportement confus. useEffect existe précisément pour isoler ce travail et l'exécuter au moment opportun.
Comment le tableau de dépendances contrôle le timing
Le deuxième argument de useEffect est le tableau de dépendances, et c'est la principale source de confusion pour les développeurs passant des composants de classe aux composants fonctionnels. Considérez-le comme un ensemble de variables que React surveille pour décider s'il doit ignorer ou exécuter votre effet après le rendu actuel.
Il existe trois modèles que vous utiliserez de manière répétée.
Aucun tableau de dépendances. Si vous omettez complètement le tableau, React suppose que vous voulez que l'effet s'exécute après chaque rendu, y compris le premier. C'est rarement ce dont vous avez besoin. Si votre effet effectue une requête réseau ou une opération DOM lourde, l'exécuter à chaque frappe de touche ou modification d'état fera chuter les performances. Utilisez ce modèle uniquement lorsque vous avez réellement besoin de réexécuter quelque chose parce que n'importe quelle prop ou état pourrait avoir changé et que vous ne pouvez pas spécifier lesquels.
Un tableau vide []. Cela indique à React d'exécuter l'effet une seule fois, immédiatement après le montage du composant et lorsque le DOM est prêt. C'est l'endroit idéal pour les récupérations de données initiales. Par exemple, si votre composant charge les données du profil utilisateur, vous voulez que cette requête soit lancée exactement une fois lorsque la page de profil apparaît, et non chaque fois que l'utilisateur interagit avec un formulaire plus bas dans la page.
Un tableau avec des variables spécifiques [count]. C'est l'outil de précision. React compare les valeurs actuelles de ces dépendances avec leurs valeurs lors du dernier rendu. Si l'une d'entre elles a changé, l'effet s'exécute. Si rien dans la liste n'a changé, React ignore complètement l'effet.
Si vous synchronisez le titre de l'onglet du navigateur avec une variable d'état, vous placeriez cette variable dans le tableau de dépendances. React mettra alors à jour le titre uniquement lorsque cette valeur change. Si vous l'oubliez, le titre restera obsolète. Si vous y mettez des variables d'état non liées, vous gaspillez des cycles à mettre à jour le titre pour des changements qui n'ont pas d'importance.
Le nettoyage n'est pas optionnel
Certains effets laissent des traces derrière eux. Un minuteur continue de compter. Un écouteur d'événements continue de se déclencher. Un WebSocket reste ouvert. Lorsque votre composant se démonte (unmount), ou même lorsqu'un effet est réexécuté parce que ses dépendances ont changé, React ne nettoie pas automatiquement les résidus de l'effet précédent. C'est votre travail.
Vous créez une fonction de nettoyage en retournant une fonction à l'intérieur de useEffect. React appelle ce nettoyage avant d'appliquer l'effet suivant, et une fois de plus lorsque le composant quitte l'écran.
Vous devriez utiliser le nettoyage pour :
- Effacer les intervalles ou les délais avec
clearIntervalouclearTimeout - Supprimer les écouteurs d'événements ajoutés à
window,documentou des nœuds externes - Se désabonner de flux de données ou de services
Négligez cela, et vous aurez des fuites de mémoire (memory leaks). Un composant se monte, attache un écouteur de défilement (scroll), se démonte, et l'écouteur reste actif. Le navigateur conserve le callback et les nœuds du DOM auxquels il fait référence. Avec le temps, surtout dans les applications monopages (SPA) avec une navigation intensive, ces "fantômes" s'accumulent et ralentissent l'onglet. La solution ne nécessite généralement que quelques lignes : retournez une fonction qui supprime ce que vous avez ajouté.
Erreurs courantes qui arrivent en production
Même les développeurs expérimentés ont recours à useEffect alors qu'une option plus simple existe. Voici trois modèles qui devraient constituer un signal d'alerte lors d'une revue de code.
Boucles infinies. Ne mettez jamais à jour une variable d'état à l'intérieur de useEffect si cette même variable figure dans votre tableau de dépendances, à moins d'avoir une condition de garde qui interrompt le cycle. Si vous lisez count, que vous l'incrémentez et que vous listez count comme dépendance, React détecte le changement, effectue un nouveau rendu, exécute à nouveau l'effet, l'incrémente encore, et finit par bloquer le navigateur.
Effets inutiles. N'utilisez pas useEffect pour calculer une valeur à partir de props ou d'un état existant. Si vous pouvez la dériver directement pendant le rendu, faites-le simplement. Les valeurs dérivées doivent se trouver dans le corps du composant ou dans un calcul mémoïsé avec useMemo. Les déplacer dans un effet divise votre logique entre les phases de rendu et d'effet sans aucun avantage, ce qui rend le code plus difficile à suivre.
Le mauvais outil pour les actions utilisateur. `use
