Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Wann Effekte ausgeführt werden, Bereinigungszeitpunkt und Abhängigkeitsverfolgung.
useEffect ist der primäre Hook zur Synchronisierung von React-Komponenten mit externen Systemen — Browser-APIs, Drittanbieter-Bibliotheken, Netzwerkanfragen und Subscriptions. Es läuft, nachdem der Browser den Bildschirm gezeichnet hat, und ist damit nicht-blockierend. Jeder Effect gibt optional eine Bereinigungsfunktion zurück, die React aufruft, bevor der Effect erneut ausgeführt wird und wenn die Komponente unmountet.
React schreibt DOM-Mutationen, der Browser zeichnet den Bildschirm, und dann führt React asynchron Ihre useEffect-Callbacks aus. Diese Reihenfolge bedeutet, dass Effects das initiale Rendering nie blockieren — der Benutzer sieht Inhalte sofort.
React vergleicht das aktuelle Deps-Array mit dem des vorherigen Renderings mithilfe von Object.is()-Gleichheit. Wenn sich ein Dep geändert hat, wird der Effect zur erneuten Ausführung eingeplant. Kein Deps-Array = nach jedem Rendering ausführen. Leeres Array [] = einmal nach dem Mount ausführen.
Wenn Ihr Effect eine Funktion zurückgibt, speichert React sie als Bereinigung. Bevor der Effect erneut ausgeführt wird (weil sich ein Dep geändert hat), ruft React zuerst die vorherige Bereinigung auf. Wenn die Komponente unmountet, ruft React die letzte Bereinigung auf.
Im StrictMode mountet React jede Komponente absichtlich → unmountet → remountet sie, um Effects zu finden, die nicht ordnungsgemäß bereinigt werden. Die doppelte Ausführung findet nur in der Entwicklung statt. Deshalb ist ordnungsgemäße Bereinigung unverzichtbar.
useLayoutEffect hat identische Semantik wie useEffect, wird aber synchron nach DOM-Mutationen und vor dem Browser-Paint ausgeführt. Verwenden Sie es, wenn Ihr Effect DOM-Messungen lesen und Korrekturen anwenden muss, bevor der Benutzer etwas sieht.
Steuert die Häufigkeit der Neuausführung. [] = nur beim Mount. [a, b] = wenn a oder b sich ändert. Kein Array = bei jedem Rendering. React vergleicht mit Object.is() — vermeiden Sie Objekte/Arrays als Deps.
Die von Ihrem Effect zurückgegebene Funktion. Wird aufgerufen, bevor der Effect erneut läuft (Dep geändert) und wenn die Komponente unmountet. Zum Abbrechen von Fetches, Löschen von Timern, Abbestellen.
Wenn ein Effect Zustand/Props aus einem alten Rendering erfasst, aber nie erneut läuft, um die aktualisierten Werte zu erhalten. Behoben, indem die Variable zu Deps hinzugefügt oder eine Ref verwendet wird.
Wird synchron nach DOM-Mutation vor dem Paint ausgeführt. Blockiert das Zeichnen — schnell halten. Für DOM-Messungen (getBoundingClientRect, Scroll-Position) verwenden.
Wird vor jeder DOM-Mutation ausgeführt. Für CSS-in-JS-Bibliotheken vorgesehen, um Style-Regeln synchron vor dem Layout einzufügen. Nicht für allgemeinen Anwendungscode.
1// 1. Event subscription — must clean up2useEffect(() => {3 window.addEventListener('resize', handleResize);4 return () => window.removeEventListener('resize', handleResize);5}, []); // mount once, clean up on unmount67// 2. Fetch with cancellation (AbortController)8useEffect(() => {9 const controller = new AbortController();1011 fetch('/api/data', { signal: controller.signal })12 .then(res => res.json())13 .then(setData)14 .catch(err => {15 if (err.name !== 'AbortError') throw err; // ignore cancellation16 });1718 return () => controller.abort(); // cancel if dep changes or unmount19}, [userId]);2021// 3. useLayoutEffect for DOM measurement22useLayoutEffect(() => {23 const height = ref.current.getBoundingClientRect().height;24 setContainerHeight(height); // applied before paint — no flicker25}, [content]);
useEffect ist der am häufigsten falsch verwendete Hook in React. Effects richtig zu handhaben — korrekte Deps, ordnungsgemäße Bereinigung, richtiges Timing — ist entscheidend für Apps ohne Speicherlecks, ohne zusätzliche Netzwerkanfragen und ohne veraltete Daten. Der mentale Modellwechsel ist: 'Effects sind Synchronisierungsmechanismen', nicht 'Lifecycle-Events'. Sie teilen React mit, wie es mit der Außenwelt synchron bleiben soll, nicht Callbacks in Mount/Update/Unmount einzuhängen.
A developer writes `useEffect(() => { fetchData() })` without a dependency array. The app makes thousands of API calls per second, the browser freezes, and the API rate limits the user.
No dependency array means the effect runs AFTER EVERY RENDER. fetchData sets state → state change triggers re-render → re-render fires the effect → effect calls fetchData → sets state → infinite loop. Each loop iteration also creates a new network request.
Add the correct dependency array: `useEffect(() => { fetchData() }, [])` for mount-only, or `useEffect(() => { fetchData(query) }, [query])` for re-fetch when query changes. The dependency array tells React's effect scheduler when to skip re-execution.
Takeaway: The dependency array is not optional decoration — it's the core control mechanism of useEffect. No array = every render. Empty array = mount only. Specific deps = run when those values change. Getting this wrong is the #1 source of useEffect bugs.
A countdown timer displays the time remaining. You use `setInterval` inside useEffect with an empty dependency array. The timer shows the same number forever instead of counting down.
The useEffect callback closes over the initial `count` value (e.g., 60). setInterval calls `setCount(count - 1)` every second, but `count` is always 60 inside the closure (captured at mount time). Result: `setCount(59)` every second — never changes from 59.
Use the functional updater form: `setCount(prev => prev - 1)`. The updater receives the CURRENT state, not the stale closure value. Alternatively, store count in a ref and sync it every render. The functional form is React's escape hatch for stale closures in effects.
Takeaway: Effects capture values at the time they're created (closures). For intervals and timeouts that need current state, always use the functional updater `setState(prev => ...)` or a ref pattern to bypass the stale closure.