React useEffect qui se déclenche deux fois : comment comprendre ce comportement en développement ?
Un effet qui semble démarrer deux fois peut donner l’impression d’un appel réseau dupliqué ou d’un minuteur incontrôlable. Dans une application React lancée en local, ce constat s’explique souvent par le Strict Mode : en mode développement, React rejoue le cycle de mise en place et de nettoyage d’un effet afin de vérifier que le composant supporte correctement un démontage puis un nouveau montage.
La distinction essentielle est la suivante : la double exécution n’est pas, à elle seule, la preuve d’un bug dans React. Elle peut toutefois révéler un effet secondaire mal maîtrisé, une dépendance instable ou un nettoyage absent. Pour comprendre le comportement, il faut examiner dans quel ordre s’effectuent le rendu des composants, la mise en place de l’effet et son nettoyage, puis comparer ce scénario au comportement en production.
En bref
- Le Strict Mode peut rejouer les effets au montage, uniquement en développement.
- Le cycle observé est généralement : mise en place, nettoyage, puis nouvelle mise en place.
- Un effet doit annuler ou fermer ce qu’il a démarré, par exemple un minuteur ou une connexion.
- Une ref destinée à empêcher la seconde exécution masque souvent le problème au lieu de le corriger.
- Les dépendances doivent refléter les valeurs réellement lues par l’effet.
React useEffect : comprendre son rôle avant de parler de double exécution
useEffect sert à synchroniser un composant avec un système extérieur à React. Cela peut être un minuteur, une connexion WebSocket, un écouteur d’événement dans le navigateur ou une requête déclenchée par un changement de données. L’effet est exécuté après le rendu et la mise à jour du DOM, lorsque l’interface correspondant au nouvel état est disponible.
Le corps d’un composant a une autre fonction : il décrit ce que l’écran doit afficher à partir des propriétés et de l’état courant. Il ne devrait pas démarrer une connexion ni installer un écouteur à chaque passage. React peut rappeler le composant pour produire un nouveau rendu, et ces opérations répétées dans le corps risqueraient alors de s’accumuler.
Dans un composant affichant un compteur, par exemple, un effet dépendant de la valeur du compteur peut journaliser cette valeur après le rendu. Sans tableau de dépendances, l’effet s’exécute après chaque rendu. Avec un tableau vide, il est associé au montage initial, tandis qu’avec une dépendance comme le compteur, il se relance lorsque cette valeur change.
Le tableau n’est pas une commande permettant de réduire arbitrairement le nombre d’exécutions. Il décrit les valeurs utilisées par la fonction de mise en place : état, propriétés et valeurs calculées. React compare les dépendances successives avec Object.is. Deux nombres identiques sont égaux au sens attendu, mais deux objets nouvellement créés peuvent être considérés comme différents, même s’ils contiennent les mêmes propriétés.
Le cycle de vie d’un effet en pratique
Un effet peut renvoyer une fonction de nettoyage. React appelle celle-ci avant de relancer la mise en place lorsque les dépendances changent, puis lorsque le composant est retiré de l’écran. Pour un salon de discussion, la mise en place ouvre une connexion et le nettoyage la ferme. Si l’utilisateur passe du salon « général » au salon « musique », l’ancienne connexion doit être fermée avant d’ouvrir la nouvelle.
Cette logique permet de distinguer le cycle de vie du composant et celui de la ressource extérieure. La connexion n’est pas simplement « lancée une fois » : elle existe tant que le composant en a besoin, et doit disparaître lorsque ce besoin change. C’est cette symétrie entre démarrage et arrêt qui rend un effet robuste.
Les effets ne sont pas nécessaires pour toutes les opérations. Une valeur dérivée de deux propriétés, comme un nom complet construit à partir d’un prénom et d’un nom, peut être calculée directement pendant le rendu. De même, une action liée à un clic, telle que l’envoi d’un formulaire, appartient généralement au gestionnaire de cet événement plutôt qu’à un effet réagissant indirectement à une modification d’état.
En somme, useEffect n’est pas un endroit où placer du code simplement parce qu’il doit s’exécuter après l’affichage : il sert à maintenir une synchronisation avec quelque chose qui se trouve hors de React.

Strict Mode et useEffect : pourquoi l’effet repart deux fois en développement
Quand le Strict Mode est activé, React ajoute en développement des vérifications destinées à faire apparaître des problèmes difficiles à repérer autrement. Pour les effets, le test consiste à exécuter la mise en place, à appeler le nettoyage, puis à refaire la mise en place. Dans la console, une connexion de test peut ainsi produire une séquence du type : ouverture, fermeture, ouverture.
Ce cycle supplémentaire se produit au montage initial d’un composant concerné. Il ne signifie pas que chaque interaction de l’utilisateur déclenche automatiquement deux appels réseau, ni que la même règle s’applique dans une version de production. Le but est de vérifier qu’un effet peut être interrompu puis rétabli sans laisser de ressources abandonnées ou d’état incohérent.
Imaginez une petite application de réservation qui ouvre un flux d’actualités pour afficher les disponibilités. Si l’effet ouvre une connexion sans jamais la fermer, le test du Strict Mode met rapidement le défaut en évidence : deux connexions peuvent rester actives. Avec un nettoyage correct, la première connexion est fermée pendant le cycle de vérification, puis la seconde prend le relais comme prévu.
Ne pas confondre les rendus et les effets
Le Strict Mode peut également rappeler certaines fonctions de rendu en développement afin de détecter les calculs qui produisent des effets secondaires. Ces rappels ne correspondent pas tous à une exécution visible de useEffect. Le rendu doit rester pur : pour les mêmes propriétés et le même état, il doit décrire la même interface, sans modifier directement un serveur, le DOM ou une variable globale.
Pour diagnostiquer une console qui affiche plusieurs messages, il est donc utile d’identifier précisément leur origine. Un message placé dans le corps du composant signale un rendu ; un message placé dans la fonction de mise en place signale un effet ; un message situé dans la fonction renvoyée signale un nettoyage. Cette séparation évite d’attribuer à useEffect un doublon provenant en réalité d’un rendu répété.
Le Strict Mode ne constitue pas une simulation exhaustive de toutes les situations possibles, mais son test du cycle mise en place-nettoyage-mise en place expose des hypothèses fragiles. Si une interface fonctionne uniquement parce que le composant ne sera jamais démonté puis remonté, son comportement peut déjà être vulnérable lors d’une navigation ou d’un changement de vue.
Il est possible de désactiver le Strict Mode, mais ce choix retire une vérification utile sans réparer l’effet en cause. Un diagnostic plus pertinent consiste à confirmer que le doublon apparaît uniquement en développement, puis à vérifier le nettoyage et les dépendances avant de modifier la configuration.
Le test du Strict Mode doit donc être lu comme un signal de robustesse : l’effet doit pouvoir être rejoué sans créer de connexion fantôme ni d’action involontaire.
Nettoyage des effets : éviter les connexions, minuteurs et écouteurs en double
Les effets secondaires les plus faciles à dupliquer sont ceux qui installent une ressource durable. Un appel à setInterval continue après le rendu qui l’a créé ; un écouteur ajouté à window reste actif ; une connexion réseau conserve ses propres événements. Si rien ne les annule, un montage répété peut laisser plusieurs instances agir sur le même composant ou sur la même page.
Pour un minuteur, le nettoyage appelle clearInterval avec l’identifiant renvoyé par setInterval. Pour un événement clavier, il retire avec removeEventListener la même fonction que celle transmise à addEventListener. Pour une connexion, il appelle la méthode de fermeture correspondante. Le nettoyage doit porter sur la ressource précise créée par cette exécution, pas sur une ressource approximative.
Exemple : suivre un salon de discussion
Supposons qu’un composant reçoive une propriété room. Son effet établit une connexion à ce salon et renvoie une fonction qui la ferme. Lorsque room change, React exécute d’abord le nettoyage de l’ancienne valeur, puis la mise en place associée à la nouvelle. Lorsque le composant disparaît, le nettoyage s’exécute une dernière fois.
La même structure s’applique à un minuteur d’interface. Une horloge peut incrémenter un état chaque seconde, mais le minuteur doit être arrêté lorsque l’utilisateur masque le composant. Sans clearInterval, chaque réapparition peut lancer un nouveau minuteur tandis que les anciens continuent en arrière-plan. L’interface peut alors accélérer et la console montrer plusieurs « ticks » pour une seule seconde.
Pour mettre à jour un compteur depuis un intervalle, une fonction de mise à jour de l’état telle que « état précédent plus un » évite de dépendre d’une valeur capturée lors du premier rendu. Cette forme est utile quand l’effet reste en place mais doit toujours calculer à partir de l’état le plus récent. Elle ne dispense toutefois pas du nettoyage du minuteur.
Dans un projet où des effets pilotent des appels réseau, le nettoyage peut aussi invalider une requête devenue obsolète ou utiliser une annulation adaptée à la bibliothèque employée. Il faut distinguer cette précaution d’un simple blocage du second appel : deux requêtes lancées puis ignorées ne sont pas nécessairement équivalentes à une requête annulée, notamment si le serveur effectue une opération d’écriture.
Les effets liés aux paiements ou à la création de données méritent une attention particulière. Une action qui doit se produire une fois à la suite d’un clic doit généralement être déclenchée par le gestionnaire de clic, et non par un effet qui observe ensuite un état. Pour approfondir les échanges entre interface et service externe, un exemple de configuration de Stripe dans Next.js peut aider à distinguer les responsabilités de l’interface et celles de l’API.
Un nettoyage correct rend la double exécution du Strict Mode inoffensive : chaque démarrage possède un arrêt correspondant, et aucune ressource ne survit au composant qui l’a créée.
Dépendances de useEffect : éviter les relances involontaires et les boucles
Un effet peut se relancer plus souvent que prévu même sans Strict Mode. Une cause fréquente est un tableau de dépendances incomplet ou, à l’inverse, une dépendance qui change à chaque rendu. Omettre une valeur lue par l’effet peut lui faire réutiliser une ancienne valeur. La règle de lint react-hooks/exhaustive-deps aide à repérer ce décalage ; il vaut mieux comprendre l’avertissement que supprimer la dépendance pour obtenir artificiellement moins d’exécutions.
Les objets et les fonctions créés dans le corps du composant sont particulièrement concernés. Un objet options reconstruit à chaque rendu possède une nouvelle identité. Si l’effet dépend de cet objet, il peut repartir après un changement d’apparence sans rapport avec la recherche qu’il effectue. Une correction courante consiste à construire l’objet dans l’effet et à ne dépendre que des valeurs primitives réellement nécessaires, comme une chaîne de recherche.
Repérer une boucle de rendu
Une boucle apparaît quand un effet modifie un état dont il dépend, puis que cette modification provoque un nouveau rendu et une nouvelle exécution de l’effet. Par exemple, un effet dépendant de count qui incrémente count à chaque passage peut recommencer sans fin. React signale souvent une profondeur de mise à jour dépassée en développement, mais le meilleur remède consiste à revoir le besoin fonctionnel plutôt qu’à tenter de masquer l’alerte.
Si la valeur peut être calculée à partir des propriétés et de l’état existants, il est préférable de la calculer pendant le rendu. Si le code doit répondre à une action précise, il doit être placé dans le gestionnaire de cette action. Si un effet doit effectuer une mise à jour initiale, une fonction de mise à jour fondée sur l’état précédent peut parfois éviter de lire une valeur qui ne devrait pas être une dépendance.
Une ref utilisée comme drapeau pour empêcher la seconde exécution du Strict Mode paraît parfois séduisante. Elle laisse toutefois l’effet sans véritable nettoyage : au prochain démontage et remontage, la ressource peut rester ouverte ou ne jamais être réinstallée. Le symptôme disparaît dans la console, mais la logique demeure fragile.
Dans certains cas plus avancés, une fonction d’événement d’effet peut permettre de lire la valeur la plus récente sans relancer la synchronisation pour chaque changement de cette valeur. Cette possibilité dépend de la version de React utilisée et de l’API disponible dans le projet. Elle ne remplace pas la règle générale : les dépendances indiquent les valeurs qui déterminent réellement la ressource synchronisée.
Le diagnostic doit ainsi partir de la question suivante : qu’est-ce qui a changé entre deux exécutions ? Si la réponse est un objet recréé, une dépendance manquante ou un état modifié par l’effet lui-même, le Strict Mode n’est probablement pas la cause principale.
| Forme du tableau | Quand l’effet se relance | Usage à vérifier |
|---|---|---|
| Pas de tableau | Après chaque rendu | À réserver aux synchronisations réellement nécessaires à chaque rendu |
| Tableau vide | Au montage, puis selon les vérifications du Strict Mode en développement | Pour une ressource indépendante des propriétés et de l’état |
| Tableau avec dépendances | Au montage et lorsque l’une des valeurs change | Pour synchroniser une connexion ou un abonnement avec ces valeurs |
Une dépendance bien choisie ne sert pas à obtenir un nombre d’appels souhaité : elle exprime clairement de quoi l’effet dépend.
Mode développement et comportement en production : une méthode de diagnostic
Lorsqu’un effet semble doublé, le premier réflexe consiste à comparer les environnements. Si le comportement se produit uniquement en mode développement avec Strict Mode activé, la séquence de vérification explique probablement l’observation. Dans une build de production, cette vérification supplémentaire n’est pas appliquée de la même façon ; cela ne dispense pas de gérer correctement les ressources ou de contrôler les requêtes déclenchées par l’application.
Une méthode simple consiste à ajouter des messages distincts au rendu, à la mise en place et au nettoyage. Il devient alors possible de lire la chronologie plutôt que de compter indistinctement les lignes de console. Il faut également vérifier si le composant est réellement monté plusieurs fois par la navigation, une condition d’affichage ou un changement de clé. Ces situations peuvent provoquer de vrais démontages et remontages, indépendamment du test du Strict Mode.
Une vérification en plusieurs étapes
- Identifier la source du message. Déterminer si le log provient du rendu, de la mise en place ou du nettoyage.
- Contrôler le tableau de dépendances. Vérifier les valeurs lues et repérer les objets ou fonctions recréés.
- Examiner les ressources ouvertes. Chaque minuteur, abonnement, écouteur ou connexion doit avoir un arrêt approprié.
- Tester un démontage réel. Masquer le composant, changer de page ou modifier sa propriété afin de confirmer que l’ancienne ressource disparaît.
- Comparer avec une build de production. Cette comparaison aide à distinguer le contrôle de développement d’un doublon fonctionnel persistant.
Une requête de lecture répétée en développement peut être visible dans l’onglet réseau, mais la solution ne consiste pas systématiquement à retirer Strict Mode. Il est préférable de comprendre si la requête est sûre à répéter, si elle peut être annulée ou mise en cache, et si elle appartient réellement à un effet. Les opérations qui modifient des données doivent être conçues avec une protection adaptée côté application et côté serveur, car l’interface ne peut pas garantir seule qu’une action n’arrivera qu’une fois.
Cette exigence rejoint les pratiques de sécurité des intégrations : une interface ne doit pas être considérée comme l’unique gardienne d’une API. Un guide consacré à la protection d’une API connectée à Zapier illustre l’importance de valider les accès et les opérations au niveau du service concerné.
Enfin, les effets ne doivent pas servir à maintenir artificiellement des données dérivées en état. Un nom affiché à partir de deux propriétés, un total calculé depuis une liste ou une classe visuelle dépendant d’un état peuvent généralement être déterminés pendant le rendu. Réduire les effets inutiles simplifie le diagnostic et limite les occasions de créer des comportements en double.
Le meilleur indicateur de qualité n’est donc pas l’absence de répétition dans une console de développement, mais la capacité de l’effet à se mettre en place, se nettoyer et se relancer sans laisser d’état incohérent.
{« @context »: »https://schema.org », »@type »: »FAQPage », »mainEntity »:[{« @type »: »Question », »name »: »Pourquoi useEffect su2019exu00e9cute-t-il deux fois au montage ? », »acceptedAnswer »:{« @type »: »Answer », »text »: »Avec Strict Mode activu00e9, React effectue en du00e9veloppement un cycle supplu00e9mentaire de mise en place, de nettoyage puis de nouvelle mise en place afin de repu00e9rer les effets qui ne gu00e8rent pas correctement les ressources quu2019ils cru00e9ent. »}},{« @type »: »Question », »name »: »La double exu00e9cution de useEffect se produit-elle en production ? », »acceptedAnswer »:{« @type »: »Answer », »text »: »La vu00e9rification supplu00e9mentaire du Strict Mode est une vu00e9rification de du00e9veloppement. Une ru00e9pu00e9tition en production peut plutu00f4t provenir du2019un vu00e9ritable remontage du composant ou du2019un changement de du00e9pendance. »}},{« @type »: »Question », »name »: »Faut-il utiliser une ref pour empu00eacher le second appel ? », »acceptedAnswer »:{« @type »: »Answer », »text »: »Non, un drapeau dans une ref masque gu00e9nu00e9ralement le symptu00f4me sans fermer une connexion ni annuler un minuteur. Il faut mettre en place un nettoyage correspondant u00e0 chaque ressource cru00e9u00e9e. »}},{« @type »: »Question », »name »: »Comment u00e9viter quu2019un effet se relance u00e0 chaque rendu ? », »acceptedAnswer »:{« @type »: »Answer », »text »: »Vu00e9rifiez que le tableau contient les valeurs ru00e9ellement utilisu00e9es et u00e9vitez du2019y du00e9pendre du2019objets ou de fonctions recru00e9u00e9s u00e0 chaque rendu. Construire ces valeurs dans lu2019effet ou calculer une donnu00e9e du00e9rivu00e9e pendant le rendu peut u00eatre plus adaptu00e9. »}}]}Pourquoi useEffect s’exécute-t-il deux fois au montage ?
Avec Strict Mode activé, React effectue en développement un cycle supplémentaire de mise en place, de nettoyage puis de nouvelle mise en place afin de repérer les effets qui ne gèrent pas correctement les ressources qu’ils créent.
La double exécution de useEffect se produit-elle en production ?
La vérification supplémentaire du Strict Mode est une vérification de développement. Une répétition en production peut plutôt provenir d’un véritable remontage du composant ou d’un changement de dépendance.
Faut-il utiliser une ref pour empêcher le second appel ?
Non, un drapeau dans une ref masque généralement le symptôme sans fermer une connexion ni annuler un minuteur. Il faut mettre en place un nettoyage correspondant à chaque ressource créée.
Comment éviter qu’un effet se relance à chaque rendu ?
Vérifiez que le tableau contient les valeurs réellement utilisées et évitez d’y dépendre d’objets ou de fonctions recréés à chaque rendu. Construire ces valeurs dans l’effet ou calculer une donnée dérivée pendant le rendu peut être plus adapté.







