<StrictMode> ist eine React-Komponente, die für alles in ihr zusätzliche Prüfungen einschaltet, nur in der Entwicklung. Sie rendert jede Komponente zweimal, führt beim Mounten das Setup, das Cleanup und erneut das Setup jedes Effekts aus, führt Ref-Callbacks zweimal aus und warnt vor veralteten APIs. Deshalb „läuft useEffect zweimal“ in der Entwicklung. In einem Produktions-Build bewirkt sie nichts.
Du schaltest sie ein, indem du deine App umschließt, meist in 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>,
);
Die Vorschauen auf dieser Seite laufen wie ein Produktions-Build, also hätte StrictMode darin keine sichtbare Wirkung. Stattdessen enthält jedes Beispiel unten die Art von Bug, für die StrictMode existiert, und lässt dich ihn von Hand auslösen. Hier fügt ein Effekt einen Klick-Listener auf document hinzu und entfernt ihn nie. Versteck und zeig das Panel ein paar Mal und klicke dann auf die Seite.
Jedes Show fügt einen weiteren Listener hinzu, und keiner wird entfernt, also loggt ein einzelner Klick einmal pro Mounten. Das ist ein Leck, das ein Nutzer verursachen kann, indem er ein Panel öffnet und schließt. StrictMode würde es schon beim allerersten Mounten zeigen: In der Entwicklung mountet er, unmountet und mountet erneut, also sähest du zwei Log-Zeilen pro Klick, bevor du den Button überhaupt anfasst.
Effekte: Setup, Cleanup, Setup
Beim Mounten führt StrictMode deinen Effekt aus, dann sein Cleanup, dann erneut den Effekt. Er simuliert, was passiert, wenn jemand einen Bildschirm verlässt und zurückkommt, und prüft, ob dein Cleanup dein Setup rückgängig macht. Wenn ja, hinterlässt das zweite Setup alles genau so wie ein einzelnes Setup, und du siehst nichts Falsches. Wenn nicht, zeigt sich der Bug sofort.
Die Lösung für den Logger ist ein Cleanup, das dieselbe Funktion entfernt, die es hinzugefügt hat:
Schalte so oft um, wie du willst: Jeder Klick loggt eine Zeile, solange der Logger sichtbar ist. Unter StrictMode würde die Konsole in der Entwicklung mit setup, cleanup, setup beginnen, und die App würde sich trotzdem korrekt verhalten. Diese Abfolge ist das Zeichen eines gesunden Effekts, kein Problem, das man abschalten müsste.
Häufige Effekte, die ein Cleanup brauchen: Event-Listener, setInterval und setTimeout, Abonnements, WebSocket- oder Chat-Verbindungen und Anfragen, deren Antwort ignoriert werden soll, wenn die Komponente weg ist. Die Seite zu useEffect zeigt das Muster für das Cleanup bei jedem davon.
Renders: zweimal aufgerufen
StrictMode ruft außerdem deine Komponentenfunktion bei jedem Render zweimal auf (und die Funktionen, die du an useState, useMemo und useReducer übergibst, sowie constructor, render und shouldComponentUpdate von Klassen). React behält das Ergebnis eines Aufrufs und wirft das andere weg. Eine pure Komponente gibt beide Male dasselbe JSX zurück, also ändert sich nichts.
Eine Komponente, die beim Rendern etwas außerhalb von sich ändert, ist nicht pur, und ein doppelter Aufruf lässt die Änderung doppelt passieren. Diese hier fügt einem Array, das sie als Prop erhält, einen Eintrag „rendered at“ hinzu:
Jeder erneute Render fügt einen Eintrag hinzu, um den niemand gebeten hat. Hier fällt es dir erst nach dem Klicken auf. Unter StrictMode würde schon der erste Render zwei neue Einträge statt einem zeigen. Die Lösung ist, beim Rendern zu berechnen, ohne zu mutieren: const shown = [...entries, 'extra'].
React 19 ergänzt ein Detail: Beim doppelten Render verwenden useMemo und useCallback das Ergebnis des ersten Aufrufs wieder, statt es zweimal zu berechnen.
Ref-Callbacks
Seit React 19 führt StrictMode auch Ref-Callbacks beim Mounten zweimal aus: anhängen, Cleanup, anhängen. Ein Ref-Callback, der einen Listener hinzufügt oder den Knoten irgendwo registriert, muss ein Cleanup zurückgeben, genau wie ein Effekt:
<div
ref={(node) => {
const observer = new ResizeObserver(onResize);
observer.observe(node);
return () => observer.disconnect();
}}
/>
Warnungen zu veralteten APIs
StrictMode warnt vor APIs, die auf dem Weg nach draußen sind, vor allem in Klassenkomponenten, etwa die alten Lifecycles UNSAFE_componentWillMount, UNSAFE_componentWillReceiveProps und UNSAFE_componentWillUpdate. Einige APIs, vor denen er früher warnte, etwa String-Refs und findDOMNode, wurden in React 19 ganz entfernt.
Für einen Teil der App einschalten
<StrictMode> ist eine normale Komponente, also kannst du nur einen Teil des Baums umschließen. Das hilft beim Einführen in einer großen App: Beginne mit neuen Bildschirmen und erweitere dann.
function App() {
return (
<>
<Header />
<StrictMode>
<Sidebar />
<Content />
</StrictMode>
<Footer />
</>
);
}
Sidebar und Content (und alles, was sie rendern) bekommen die Prüfungen, Header und Footer nicht. Es gibt keinen Weg, sie für einen Teilbaum innerhalb eines strikten Teils abzuschalten.
Wo er standardmäßig eingeschaltet ist:
- Vite: Das React-Template umschließt
<App />inmain.jsxmit<StrictMode>. - Next.js: Der App Router schaltet ihn standardmäßig ein;
reactStrictModeinnext.config.jssteuert ihn.
In der Produktion bewirkt er nichts
Ein Produktions-Build ignoriert <StrictMode>: ein Render, ein Durchlauf pro Effekt, keine Warnungen. Die doppelten Aufrufe kosten deine Nutzer also nie etwas, und es gibt keinen Grund, den Wrapper vor dem Deployen zu entfernen.
Wenn ein doppelter Effekt in der Entwicklung etwas kaputt macht, etwa eine doppelt gesendete Anfrage oder ein doppelt geloggtes Analytics-Event, ist das eine Information. Meist ist die Lösung ein Cleanup (ein Flag ignore für eine Anfrage, ein Trennen für eine Verbindung). Für etwas, das wirklich nur einmal pro App-Start passieren darf, etwa das Initialisieren eines SDK, führe es außerhalb jeder Komponente auf Modulebene aus oder schütze es mit einem Flag auf Modulebene.
let didInit = false;
function App() {
useEffect(() => {
if (didInit) return;
didInit = true;
initAnalytics(); // runs once per page load, even in StrictMode
}, []);
}
Meide den verbreiteten Workaround mit einer Ref, die den zweiten Effektdurchlauf überspringt. Er versteckt die Warnung, ohne das fehlende Cleanup zu beheben, und der Bug kommt zurück, sobald die Komponente zum ersten Mal wirklich unmountet und erneut mountet, wie der Logger oben.
Häufig gestellte Fragen
Was macht React StrictMode?
Nur in der Entwicklung rendert er jede Komponente in ihm zweimal, führt beim Mounten das Setup, das Cleanup und erneut das Setup jedes Effekts aus, führt Ref-Callbacks auf dieselbe Weise zweimal aus und warnt vor veralteten APIs. Die zusätzlichen Aufrufe decken unpure Renders und Effekte mit fehlendem Cleanup auf.
Warum läuft mein useEffect zweimal?
Weil die Komponente in <StrictMode> steckt und du einen Entwicklungs-Build ausführst. React mountet sie, simuliert ein Unmount (dein Cleanup läuft) und mountet sie erneut. Wenn das zweite Setup einen Bug verursacht, fehlt dem Effekt ein Cleanup.
Wirkt sich StrictMode auf die Produktion aus?
Nein. Ein Produktions-Build rendert einmal und führt jeden Effekt einmal aus, mit oder ohne <StrictMode>. Du kannst ihn im Code lassen.
Wie deaktiviere ich StrictMode?
Entferne den Wrapper <StrictMode>, meist in main.jsx oder index.js, oder setze reactStrictMode: false in next.config.js. Das Cleanup zu beheben ist meist besser: Der doppelte Durchlauf zeigt auf einen Bug, auf den echte Nutzer stoßen können.
Warum wird console.log zweimal ausgegeben?
StrictMode ruft deine Komponentenfunktion in der Entwicklung pro Render zweimal auf. Die React DevTools können die Logs des zweiten Aufrufs in ihren Einstellungen ausgrauen oder ausblenden.