Menu

Strict Mode en React : pourquoi tout s'exécute deux fois

StrictMode est une enveloppe réservée au développement qui fait le rendu des composants deux fois, exécute au montage la mise en place, le nettoyage puis à nouveau la mise en place des effets, et avertit au sujet des API obsolètes, pour que les rendus impurs et les nettoyages manquants apparaissent tôt. Il ne fait rien en production.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

<StrictMode> est un composant React qui active des vérifications supplémentaires pour tout ce qu'il contient, en développement uniquement. Il fait le rendu de chaque composant deux fois, exécute au montage la mise en place, le nettoyage puis à nouveau la mise en place de chaque effet, exécute les ref callbacks deux fois et avertit au sujet des API obsolètes. C'est pourquoi useEffect « s'exécute deux fois » en développement. Dans un build de production, il ne fait rien.

Vous l'activez en enveloppant votre application, généralement dans main.jsx :

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';

createRoot(document.getElementById('root')).render(
    <StrictMode>
        <App />
    </StrictMode>,
);

Les aperçus de cette page fonctionnent comme un build de production, StrictMode n'y aurait donc aucun effet visible. À la place, chaque exemple ci-dessous contient le type de bug que StrictMode est fait pour détecter, et vous permet de le déclencher à la main. Ici, un effet ajoute un écouteur de clic sur document et ne le retire jamais. Masquez et affichez le panneau deux ou trois fois, puis cliquez sur la page.

Chaque Show ajoute un écouteur de plus, et aucun n'est retiré, donc un seul clic écrit une fois par montage. C'est une fuite qu'un utilisateur pourrait provoquer en ouvrant et fermant un panneau. StrictMode la montrerait dès le tout premier montage : en développement, il monte, démonte et monte à nouveau, vous verriez donc deux lignes par clic avant même de toucher au bouton.

Effets : mise en place, nettoyage, mise en place

Au montage, StrictMode exécute votre effet, puis son nettoyage, puis à nouveau l'effet. Il simule ce qui se passe quand un utilisateur quitte un écran puis y revient, et vérifie que votre nettoyage défait votre mise en place. Si c'est le cas, la seconde mise en place laisse les choses exactement comme une seule le ferait, et vous ne voyez rien d'anormal. Sinon, le bug apparaît tout de suite.

La correction pour le logger est un nettoyage qui retire la même fonction que celle ajoutée :

Basculez autant que vous voulez : chaque clic écrit une ligne tant que le logger est affiché. Sous StrictMode en développement, la console commencerait par setup, cleanup, setup, et l'application se comporterait quand même correctement. Cette séquence est le signe d'un effet sain, pas un problème à désactiver.

Effets courants qui ont besoin d'un nettoyage : écouteurs d'événements, setInterval et setTimeout, abonnements, connexions WebSocket ou de chat, et requêtes dont la réponse doit être ignorée si le composant a disparu. La page sur useEffect montre le pattern de nettoyage pour chacun.

Rendus : appelés deux fois

StrictMode appelle aussi votre fonction composant deux fois pour chaque rendu (ainsi que les fonctions que vous passez à useState, useMemo et useReducer, et les méthodes de classe constructor, render et shouldComponentUpdate). React garde le résultat d'un appel et jette l'autre. Un composant pur renvoie le même JSX les deux fois, donc rien ne change.

Un composant qui modifie quelque chose en dehors de lui pendant le rendu n'est pas pur, et l'appeler deux fois fait se produire la modification deux fois. Celui-ci ajoute une entrée « rendered at » à un tableau qu'il reçoit en prop :

Chaque nouveau rendu ajoute une entrée que personne n'a demandée. Ici, vous ne le remarquez qu'après avoir cliqué. Sous StrictMode, le premier rendu afficherait déjà deux nouvelles entrées au lieu d'une. La correction consiste à calculer pendant le rendu sans muter : const shown = [...entries, 'extra'].

React 19 ajoute un détail : pendant le double rendu, useMemo et useCallback réutilisent le résultat du premier appel au lieu de le calculer deux fois.

Ref callbacks

Depuis React 19, StrictMode exécute aussi les ref callbacks deux fois au montage : attachement, nettoyage, attachement. Une ref callback qui ajoute un écouteur ou enregistre le nœud quelque part doit renvoyer un nettoyage, exactement comme un effet :

<div
    ref={(node) => {
        const observer = new ResizeObserver(onResize);
        observer.observe(node);
        return () => observer.disconnect();
    }}
/>

Avertissements sur les API obsolètes

StrictMode avertit au sujet des API en voie de disparition, surtout dans les composants classes, comme les anciennes méthodes de cycle de vie UNSAFE_componentWillMount, UNSAFE_componentWillReceiveProps et UNSAFE_componentWillUpdate. Certaines API qu'il signalait, comme les string refs et findDOMNode, ont été entièrement supprimées de React 19.

L'activer pour une partie de l'application

<StrictMode> est un composant normal, vous pouvez donc n'envelopper qu'une partie de l'arbre. C'est utile pour l'adopter dans une grande application : commencez par les nouveaux écrans et étendez ensuite.

function App() {
    return (
        <>
            <Header />
            <StrictMode>
                <Sidebar />
                <Content />
            </StrictMode>
            <Footer />
        </>
    );
}

Sidebar et Content (et tout ce qu'ils affichent) bénéficient des vérifications ; Header et Footer, non. Il n'existe aucun moyen de le désactiver pour un sous-arbre à l'intérieur d'une partie stricte.

Là où il est activé par défaut :

  • Vite : le modèle React enveloppe <App /> dans <StrictMode> dans main.jsx.
  • Next.js : l'App Router l'active par défaut ; reactStrictMode dans next.config.js le contrôle.

Il ne fait rien en production

Un build de production ignore <StrictMode> : un rendu, une exécution d'effet, aucun avertissement. Les doubles appels ne coûtent donc jamais rien à vos utilisateurs, et il n'y a aucune raison de retirer l'enveloppe avant le déploiement.

Si un double effet casse quelque chose en développement, comme une requête envoyée deux fois ou un événement d'analytics enregistré deux fois, c'est une information. La plupart du temps, la correction est un nettoyage (un indicateur ignore pour une requête, une déconnexion pour une connexion). Pour quelque chose qui doit vraiment se produire une fois par chargement de l'application, comme l'initialisation d'un SDK, exécutez-le hors de tout composant au niveau du module, ou protégez-le par un indicateur au niveau du module.

let didInit = false;

function App() {
    useEffect(() => {
        if (didInit) return;
        didInit = true;
        initAnalytics(); // runs once per page load, even in StrictMode
    }, []);
}

Évitez le contournement courant d'une ref qui saute la seconde exécution de l'effet. Il masque l'avertissement sans corriger le nettoyage manquant, et le bug revient la première fois que le composant est réellement démonté puis remonté, comme le logger ci-dessus.

Questions fréquentes

Que fait StrictMode dans React ?

En développement uniquement, il fait le rendu de chaque composant qu'il contient deux fois, exécute pour chaque effet la mise en place, le nettoyage puis à nouveau la mise en place au montage du composant, exécute les ref callbacks deux fois de la même façon, et avertit au sujet des API obsolètes. Ces appels supplémentaires révèlent les rendus qui ne sont pas purs et les effets auxquels il manque un nettoyage.

Pourquoi mon useEffect s'exécute-t-il deux fois ?

Parce que le composant est dans <StrictMode> et que vous utilisez un build de développement. React le monte, simule un démontage (votre nettoyage s'exécute), puis le monte à nouveau. Si la seconde mise en place provoque un bug, il manque un nettoyage à l'effet.

StrictMode affecte-t-il la production ?

Non. Un build de production fait un seul rendu et exécute chaque effet une seule fois, avec ou sans <StrictMode>. Vous pouvez le laisser dans votre code.

Comment désactiver StrictMode ?

Retirez l'enveloppe <StrictMode>, généralement dans main.jsx ou index.js, ou définissez reactStrictMode: false dans next.config.js. Corriger le nettoyage est généralement préférable : la double exécution signale un bug que de vrais utilisateurs peuvent rencontrer.

Pourquoi console.log s'affiche-t-il deux fois ?

StrictMode appelle votre fonction composant deux fois par rendu en développement. Dans leurs réglages, les React DevTools peuvent griser les messages du second appel, ou les masquer.

Illustration des langages de programmation de Coddy

Apprendre à coder avec Coddy

COMMENCER