useEffect exécute un morceau de code après que React a fait le rendu de votre composant et mis l'écran à jour. Vous l'utilisez pour garder un composant synchronisé avec quelque chose d'extérieur à React, comme un minuteur, une connexion réseau ou un écouteur d'événement, et vous contrôlez quand il s'exécute à nouveau grâce au tableau de dépendances.
Ouvrez la console sous l'aperçu : l'effet écrit une fois quand le composant apparaît, puis à nouveau après chaque clic. Remplacez [count] par [] et cliquez encore : l'effet reste silencieux après le premier message.
La syntaxe
useEffect(() => {
// setup: runs after the render is on the screen
return () => {
// cleanup: runs before the next setup, and on unmount
};
}, [dependencies]);
- Le premier argument est la fonction de mise en place (setup). Elle peut renvoyer une fonction de nettoyage, ou rien.
- Le second argument est le tableau de dépendances : chaque valeur du composant que la mise en place lit (props, état, et tout ce qui en est calculé).
- Appelez
useEffectau niveau supérieur du composant, comme tous les hooks.
Les effets s'exécutent après le rendu
Le rendu, c'est React qui appelle votre fonction composant pour savoir ce que l'écran doit afficher. Les effets attendent que ce travail soit terminé et que le DOM soit mis à jour : dans un effet, la page affiche donc déjà les nouvelles valeurs.
Chaque clic affiche d'abord le rendu, puis l'effet, et l'effet lit le texte du bouton que React vient d'écrire. C'est pourquoi lire le DOM, démarrer un minuteur ou se connecter à un serveur se fait dans un effet et pas dans le corps du composant : le corps s'exécute pendant le rendu, avant que quoi que ce soit soit à l'écran, et React peut l'appeler plus souvent que vous ne le pensez.
Les effets s'exécutent généralement après que le navigateur a affiché la page. Quand un effet doit mesurer le DOM avant l'affichage, utilisez plutôt useLayoutEffect, qui s'exécute toujours avant l'affichage.
Le tableau de dépendances : trois formes
Le tableau de dépendances décide quand l'effet s'exécute à nouveau. Il existe trois formes, et ce bloc les utilise toutes pour que vous puissiez observer la différence.
Au chargement, les trois effets écrivent. Cliquez sur a + 1 et deux d'entre eux s'exécutent : celui sans tableau et celui qui dépend de a. Cliquez sur b + 1 et seul celui sans tableau s'exécute, car rien n'a changé dans [] ni dans [a].
| Tableau de dépendances | Quand l'effet s'exécute |
|---|---|
| aucun | Après chaque rendu |
[] | Une fois, après le premier rendu (au montage) |
[a, b] | Après le premier rendu, puis chaque fois que a ou b a changé |
React compare chaque dépendance à sa valeur précédente avec Object.is. Les nombres, les chaînes et les booléens se comparent par valeur. Les objets, les tableaux et les fonctions se comparent par identité, ce qui compte beaucoup (voir plus bas).
N'omettez pas une valeur du tableau pour qu'un effet s'exécute moins souvent. L'effet continuerait d'utiliser la valeur d'un ancien rendu. La règle de lint react-hooks/exhaustive-deps le détecte ; elle fait partie de la configuration ESLint incluse dans le modèle React de Vite et dans la configuration ESLint de Next.js.
Nettoyage
Beaucoup d'effets démarrent quelque chose qu'il faut arrêter : une connexion, un abonnement, un minuteur. Renvoyez une fonction depuis l'effet et React l'appelle à deux moments : juste avant que l'effet s'exécute à nouveau avec de nouvelles dépendances, et quand le composant quitte l'écran (démontage).
Choisissez un autre salon : la console affiche disconnect from general puis connect to music. L'ancienne connexion se ferme toujours avant l'ouverture de la nouvelle. Cliquez sur Hide et le nettoyage s'exécute une dernière fois, car ChatRoom a été démonté.
La fonction connect remplace ici un vrai client de chat ou WebSocket. La structure est la même pour tous : créez-le dans la mise en place, fermez-le dans le nettoyage.
Arrêter un minuteur
Un minuteur démarré dans un effet continue de tourner après la disparition du composant, sauf si le nettoyage l'arrête.
Supprimez la ligne clearInterval(id);, puis masquez le minuteur : des tick continuent d'arriver dans la console depuis un composant qui n'existe plus. Chaque Show en démarre un autre.
Notez setSeconds((s) => s + 1). L'effet s'exécute une fois, donc un simple setSeconds(seconds + 1) lirait pour toujours seconds du premier rendu et s'arrêterait à 1. La fonction de mise à jour reçoit toujours la dernière valeur.
Retirer un écouteur d'événement
Les écouteurs d'événements sur window ou document suivent le même pattern. Passez à removeEventListener la même fonction que celle passée à addEventListener :
useEffect(() => {
function handleKeyDown(e) {
if (e.key === 'Escape') onClose();
}
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [onClose]);
La boucle infinie
Un effet qui modifie une valeur d'état dont il dépend aussi lance une boucle : l'état change, React refait le rendu, la dépendance a changé, l'effet s'exécute et modifie à nouveau l'état.
// Never stops: every render runs the effect, every effect causes a render
useEffect(() => {
setCount(count + 1);
});
// Same loop: count changes, so the effect runs again
useEffect(() => {
setCount(count + 1);
}, [count]);
Le composant refait des rendus sans fin et l'onglet reste occupé. React n'arrête pas cette boucle : en développement, il affiche une erreur "Maximum update depth exceeded" dans la console, et en production il n'affiche rien. Ce bloc exécute la seconde version avec une condition d'arrêt, pour que vous puissiez observer la boucle sans figer la page :
La console affiche de render 0 à render 5 : six rendus pour un seul montage, chacun lancé par l'effet. Les corrections dépendent de ce que vous vouliez :
- Vous le vouliez une seule fois. Utilisez
[]et la forme fonction de mise à jour,setCount((c) => c + 1), pour quecountne soit pas une dépendance. - Vous calculez une valeur à partir d'un autre état. Ne la stockez pas du tout ; calculez-la pendant le rendu, comme le montre la dernière section de cette page.
- Vous réagissez à une action de l'utilisateur. Placez le code dans le gestionnaire d'événement.
Objets et fonctions comme dépendances
Un objet, un tableau ou une fonction créé dans le corps du composant est une nouvelle valeur à chaque rendu, donc un effet qui en dépend s'exécute après chaque rendu, même quand rien n'a changé à l'intérieur.
Changer les couleurs n'a rien à voir avec la recherche, pourtant chaque bascule affiche de nouveau search for react. Déplacez la ligne options dans l'effet et remplacez le tableau par [query] : les bascules deviennent silencieuses et seule la saisie dans le champ lance la recherche.
C'est la correction générale. Créez les objets et les fonctions à l'intérieur de l'effet qui les utilise, et dépendez des valeurs primitives à partir desquelles ils sont construits. Quand une fonction doit vivre hors de l'effet, useCallback garde son identité stable d'un rendu à l'autre.
Pourquoi les effets s'exécutent deux fois en développement
Avec <StrictMode> activé (une nouvelle application Vite enveloppe la racine dedans, et l'App Router de Next.js l'active par défaut), React fait un test en développement : il monte chaque composant, exécute ses effets, exécute leurs nettoyages, puis exécute à nouveau les effets. Pour le salon de chat ci-dessus, la console de développement affiche :
connect to general
disconnect from general
connect to general
Cela arrive une fois, au montage, et seulement en développement. Le but est de révéler les effets auxquels il manque un nettoyage : si deux exécutions d'affilée cassent quelque chose, l'utilisateur le casserait aussi en partant puis en revenant. La solution n'est jamais de bloquer la seconde exécution avec une ref ; c'est d'écrire le nettoyage. L'aperçu de cette page fonctionne comme un build de production, vous ne verrez donc pas la double exécution ici.
Vous n'avez peut-être pas besoin d'un effet
Les effets servent à se synchroniser avec des systèmes extérieurs à React. Deux usages courants se portent mieux sans effet.
Les valeurs calculées à partir des props ou de l'état. Calculez-les pendant le rendu. Les stocker dans l'état et les synchroniser avec un effet coûte un rendu supplémentaire, et le premier de ces rendus place une valeur périmée dans le DOM :
Chaque clic fait faire deux rendus à WithEffect, et son premier rendu affiche encore l'ancien nom. Computed fait un seul rendu, avec la bonne valeur.
Le code qui répond à une action de l'utilisateur. Envoyer un formulaire, afficher une notification après un achat ou envoyer un événement d'analyse pour un clic, cela va dans le gestionnaire d'événement. Là, vous savez exactement ce qui s'est passé, alors qu'un effet sait seulement qu'une valeur a changé.
useEffectEvent
Parfois, un effet a besoin de la dernière valeur d'une prop sans se relancer quand cette prop change. Dans le salon de chat, une notification doit utiliser le theme actuel, mais changer de thème ne doit pas provoquer de reconnexion. React 19.2 ajoute useEffectEvent pour cela :
import { useEffect, useEffectEvent } from 'react';
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme); // always the latest theme
});
useEffect(() => {
const connection = createConnection(roomId);
connection.on('connected', () => onConnected());
connection.connect();
return () => connection.disconnect();
}, [roomId]); // theme is not a dependency
}
Une fonction issue de useEffectEvent ne sert qu'à être appelée dans des effets. Ne la passez pas à d'autres composants et ne la listez pas dans un tableau de dépendances.
Questions fréquentes
Quand useEffect s'exécute-t-il ?
Après que React a fait le rendu du composant et mis à jour le DOM, en général après que le navigateur a affiché la page. Sans tableau de dépendances, il s'exécute après chaque rendu ; avec [], seulement après le premier ; et avec [a, b], après le premier rendu et après tout rendu où a ou b a changé.
Qu'est-ce que la fonction de nettoyage de useEffect ?
La fonction que vous renvoyez depuis l'effet. React l'appelle avant de relancer l'effet avec de nouvelles dépendances, et une dernière fois quand le composant est retiré de l'écran. Utilisez-la pour arrêter les minuteurs, retirer les écouteurs d'événements et fermer les abonnements.
Pourquoi mon useEffect s'exécute-t-il deux fois ?
En développement, <StrictMode> monte chaque composant, exécute ses effets, les nettoie et les exécute à nouveau, pour vous montrer les effets auxquels il manque un nettoyage. Cela arrive une fois au montage et seulement en développement. Les builds de production exécutent l'effet une seule fois.
Pourquoi mon useEffect provoque-t-il une boucle infinie ?
L'effet modifie une valeur d'état qui figure aussi dans son tableau de dépendances (ou il n'a aucun tableau). Modifier l'état provoque un nouveau rendu, la dépendance a changé, donc l'effet s'exécute à nouveau. Utilisez une fonction de mise à jour et retirez la dépendance, ajoutez une condition, ou calculez plutôt la valeur pendant le rendu.
Le callback de useEffect peut-il être async ?
Non. Le callback doit ne rien renvoyer ou renvoyer une fonction de nettoyage, et une fonction async renvoie une promesse. Définissez une fonction async à l'intérieur de l'effet et appelez-la, comme le montre la page sur la récupération de données.