Unendliche Re-Render-Schleifen in React beheben: Fallstricke bei KI-generiertem Code
Lerne, unendliche Re-Render-Schleifen in React zu beheben: erkenne typische KI-generierte State- und Effekt-Fallen und folge einem Schritt-für-Schritt-Workflow, um Updates zu stabilisieren.

Wie eine unendliche Re-Render-Schleife aussieht
Eine unendliche Re-Render-Schleife entsteht, wenn eine React-Komponente immer wieder rendert, ohne jemals zur Ruhe zu kommen. Statt eines normalen Render-Zyklus löst etwas bei jedem Schritt ein weiteres Update aus.
Die ersten Anzeichen sieht man oft im UI: die Seite wirkt eingefroren, Buttons reagieren nicht, Eingaben ruckeln oder die Oberfläche flackert, weil sich der Zustand schneller ändert, als der Browser neu zeichnen kann.
Typische technische Symptome:
- Eine rote Fehlerüberlagerung wie "Too many re-renders. React limits the number of renders to prevent an infinite loop"
- CPU-Spitzen und ein heiß oder langsam werdender Tab
- Dieselbe Netzwerk-Anfrage, die sich immer wiederholt
- Ununterbrochene Einträge in der Konsole
- UI, die sich selbst zurücksetzt (zum Beispiel ein Modal, das sofort wieder aufgeht, nachdem man es geschlossen hat)
Diese Schleifen sind mehr als ein Performance-Problem. Sie zerstören Logik. Ein Formular beendet die Validierung nie, Auth-Checks springen zwischen „eingeloggt“ und „ausgeloggt“, und Effekte, die einmal laufen sollten, werden hunderte Male ausgeführt. Das erzeugt oft weitere Bugs wie doppelte Analytics-Events, wiederholte Toaster oder Rate-Limits bei APIs.
Ein typisches Szenario: ein Dashboard lädt Daten, holt sie erneut, setzt State mit der Antwort und holt dann wieder neu, weil ein Effekt von etwas abhängt, das sich bei jedem Render ändert. Der Nutzer sieht einen Spinner, der nie endet, und das Backend bekommt eine Flut identischer Anfragen.
Warum KI-generierter React-Code oft schleift
KI-Tools können React-Code erzeugen, der in einer Demo fein aussieht, aber mit echten Daten und Nutzern auseinanderfällt. Ein großer Grund ist das Vermischen von drei Dingen, die getrennt bleiben sollten:
- Quellzustand (was Sie speichern)
- Abgeleitete Werte (was Sie aus dem gespeicherten Zustand berechnen)
- Nebeneffekte (was Sie tun, weil sich etwas geändert hat)
Wenn diese Bereiche vermischt werden, landen Sie schnell dabei, State während des Renders zu setzen oder einen Effekt auszuführen, der genau die Werte ändert, deren Änderung den Effekt erneut auslöst.
Muster 1: „Alles in einem useEffect erledigen“
KI-generierte Komponenten packen oft Fetching, Parsen, Filtern und UI-State in einen einzigen Effekt. Der Effekt läuft, ruft setState ein paarmal auf, rendert neu und läuft dann wieder, weil sich eine Abhängigkeit geändert hat.
Die Schleife sieht meist so aus: fetch -> setState -> re-render -> effect läuft erneut -> fetch erneut.
Muster 2: Instabile Abhängigkeiten, die bei jedem Render neu entstehen
Eine weitere häufige Ursache ist das Platzieren von Werten in das Dependency-Array, die bei jedem Render neu erzeugt werden, selbst wenn sie „gleich“ aussehen. Beispiele sind Inline-Objekte/-Arrays, Inline-Funktionen oder abgeleitete Werte, die bei jedem Render neu aufgebaut werden.
React vergleicht Abhängigkeiten nach Referenz, nicht nach tiefer Gleichheit. Eine neue Objekt-Referenz bedeutet „geändert“, also läuft der Effekt wieder.
Ein realistisches Szenario: ein Dashboard baut queryParams als Objekt innerhalb der Komponente und nutzt es in useEffect zum Abruf. Bei jedem Render entsteht ein neues Objekt, also feuert der Effekt wieder, fetches erneut, setzt State erneut und das Dashboard kommt nie zur Ruhe.
Wie Sie bestätigen, wo die Schleife beginnt
Bestätigen Sie zuerst, dass es sich wirklich um eine Schleife handelt und nicht nur um „viele Renders“. Fügen Sie einen Zähler oben in der Komponentenfunktion ein.
console.count('Component render')
Wenn der Zähler kontinuierlich ansteigt, obwohl Sie nicht mit der Seite interagieren, ist es bestätigt.
Als Nächstes isolieren Sie den Auslöser. Deaktivieren Sie temporär Nebeneffekte, bis die Schleife aufhört. Der schnellste Weg ist, useEffect-Blöcke nacheinander auszukommentieren (oder frühzeitig return zu machen) und nach jeder Änderung neu zu laden. Wenn die Schleife stoppt, deutet die letzte Änderung auf die Quelle hin.
Eine praktische Reihenfolge:
useEffect-Blöcke, die State setzen oder in Storage schreiben- Effekte, die APIs aufrufen oder sich irgendwo abonnieren
- Abgeleiteter State, der bei jedem Render berechnet oder synchronisiert wird
- Context-Provider und übergeordnete Komponenten
React DevTools können auch helfen. Schalten Sie "Highlight updates" ein und beobachten Sie, welcher Bereich der UI wiederholt aufblinkt. Das zeigt oft, ob die Schleife in der aktuellen Komponente oder eine Ebene darüber liegt.
Wenn die Network-Tab immer dieselbe Anfrage zeigt, ist die Kette oft: render -> effect -> fetch -> setState -> render. Das Beheben der Schleife verhindert auch doppelte Anfragen und Rate-Limit-Probleme.
Schritt-für-Schritt-Arbeitsablauf, um die Schleife zu stoppen
Behandle eine Re-Render-Schleife wie eine Kettenreaktion. Ein Update verursacht den nächsten Render, der das nächste Update verursacht. Deine Aufgabe ist es, den ersten Dominostein zu finden.
- Identifiziere das Update, das direkt vor dem nächsten Render passiert.
Beobachte, welcher Setter läuft (setUser, setItems, setLoading) und was ihn auslöst. Wenn es mehrere Setter gibt, kommentiere sie nacheinander aus, um zu sehen, welcher die Schleife stoppt.
- Stelle sicher, dass du den State nicht während des Renders aktualisierst.
Ein häufiger Fehler ist das Aufrufen eines Setters während des "Berechnens" von Werten oder in einem Helper, der beim Erstellen des JSX ausgeführt wird. State-Updates gehören in Event-Handler, Effekte oder Callbacks, nicht in den Render-Body.
- Reduziere den Effekt auf seine eigentliche Aufgabe.
Schrumpfe den Effekt auf die kleinste Version, die den Fehler noch reproduziert. Notiere, wovon er wirklich abhängt (Props, State und externe Werte). Abhängigkeitsprobleme verbergen sich oft genau hier.
- Stabilisiere die Eingaben und mache Updates idempotent.
Ein paar schnelle Fixes, die Schleifen rasch stoppen:
- Mache instabile Eingaben stabil (memoize Callbacks/Objekte oder verschiebe sie außerhalb der Komponente)
- Speichere abgeleitete Werte nicht im State, es sei denn, es ist notwendig (berechne sie aus Props/State oder nutze
useMemo) - Schütze Updates so, dass
setStatenur bei tatsächlicher Änderung aufgerufen wird - Füge Cleanup für Subscriptions, Timer und laufende Anfragen hinzu
Wenn ein Effekt einen Wert „synchronisiert“, muss er so geschrieben sein, dass er sicher mehrfach ausgeführt werden kann. Ein Effekt darf mehrfach laufen, sollte aber nur dann den State ändern, wenn sich die Eingaben wirklich geändert haben.
useEffect-Abhängigkeitsprobleme beheben
Die meisten useEffect-Schleifen lassen sich auf dasselbe Muster zurückführen: der Effekt setzt State, und diese State-Änderung führt dazu, dass der Effekt erneut läuft.
Behandle jedes setState innerhalb eines Effekts als verdächtig, bis du erklären kannst, warum es stoppt.
Wichtige Regel: setze State nicht bedingungslos innerhalb eines Effekts. Wenn der Effekt bei Mount und bei Abhängigkeitsänderungen läuft, brauchst du eine Bedingung, die wiederholte Updates verhindert.
Eine praxisnahe Absicherung ist: "nur aktualisieren, wenn der nächste Wert tatsächlich unterschiedlich ist." Das ist wichtig, wenn Code Arrays oder Objekte neu erstellt und sie im State speichert, obwohl sich der Inhalt nicht geändert hat.
Gute Fixes:
- Vor dem
setStatevergleichen (oder im funktionalen Update vergleichen) - Abhängigkeiten memoizen, wenn du wirklich von Objekten/Funktionen abhängen musst
- Abgeleitete Werte im Render berechnen statt sie per Effekt zu synchronisieren
- Abhängigkeiten bewusst klein halten (wo möglich primitive Werte)
Behandle das Dependency-Array nicht wie eine Checkliste. Linter helfen, aber ein Warnung wegzuschalten, indem man eine Abhängigkeit hinzufügt, kann einen einmaligen Setup-Effekt in eine selbst-auslösende Schleife verwandeln. Häufig ist die eigentliche Lösung eine Umstrukturierung: Teile einen großen Effekt in zwei kleinere oder verschiebe Arbeit in einen Event-Handler.
State-Updates, die unbeabsichtigt Re-Renders auslösen
Es ist leicht, sich auf useEffect zu fixieren, aber manche Schleifen entstehen durch einfache State-Fehler.
State während des Renders setzen
Wenn du setState im Render-Body aufrufst, hat React keine Wahl außer zu rendern. Das kann direkt passieren (ein einfaches setX(...)) oder indirekt (ein Helper, den du während des Renders aufrufst und der State ändert). Selbst etwas Harmloses wie „Daten normalisieren, falls sie fehlen“ kann zu einer Schleife werden.
Mirror-State (abgeleiteter State, der Props hinterherläuft)
Eine weitere Falle ist das Kopieren von Props in State und anschließendes "Synchronisieren", wenn sie nicht übereinstimmen. Ist die Prop bei jedem Render ein neues Objekt, hört die Sync-Logik nie auf.
Einige Muster, die oft wiederholte Renders verursachen:
- State beim Render aktualisieren (einschließlich Helpers, die aus JSX aufgerufen werden)
- Abgeleitete Werte im State speichern statt sie zu berechnen
- Neue Arrays/Objekte bei jedem Render erstellen und ohne Gleichheitsprüfung setzen
- Ein sich änderndes
key-Prop übergeben, das Remounts erzwingt
Wenn der nächste State vom vorherigen abhängt, nutze funktionale Updates. Statt setCount(count + 1) verwende setCount(c => c + 1). Das vermeidet veraltete Werte, die "Korrektur"-Updates auslösen können.
Wenn mehrere zusammenhängende Updates sich gegenseitig hochschaukeln (loading, errors, retries, cached data), kann useReducer helfen, weil alle Übergänge an einem Ort gebündelt sind.
Fallen bei Datenabruf, Subscriptions und Cleanup
Schleifen verbergen sich oft in „normalen“ Nebeneffekten: Fetches, Subscriptions und Timern. Wenn jeder Callback setState aufruft, kannst du konstante Re-Renders bekommen, selbst wenn das UI scheinbar gleich bleibt.
Fetches, die immer wieder neu laufen
Wenn ein Fetch an State gekoppelt ist, den der Fetch selbst aktualisiert, entsteht eine Schleife.
Mache Requests abbruchsicher, damit veraltete Antworten nicht neueren State überschreiben. Verwende AbortController im Effekt und rufe abort im Cleanup auf.
Um Duplikate zu reduzieren, füge eine einfache Sperre mit einem Ref (nicht State) hinzu, zum Beispiel ein Flag für laufende Anfragen.
Subscriptions, Timer und Cleanup
Listener und Timer können ewig feuern, wenn das Cleanup fehlt. Eine einfache Regel: jedes „Start“ braucht ein entsprechendes „Stop“ im Cleanup.
Intervalle/Timeouts löschen, von Listenern abmelden und Event-Handler entfernen.
Eine Datenquelle, ein Schreiber
Vermeide es, denselben State an mehreren Stellen zu schreiben. Wenn ein Fetch profile setzt, eine Subscription auch profile setzt und ein anderer Effekt profile synchronisiert, hast du eine Feedback-Schleife geschaffen. Wähle einen Owner für Writes; andere können ein Refresh auslösen, aber nicht denselben State schreiben.
Häufige Fehler, die die Schleife am Leben erhalten
Manche Schleifen "verschwinden", wenn du eine Zeile auskommentierst, kommen aber zurück, wenn du sie wieder einfügst. Das bedeutet meist, dass der zugrundeliegende Auslöser noch vorhanden ist.
StrictMode macht unsichere Effekte sichtbar
Wenn ein Effekt in Entwicklung zweimal feuert, erfüllt React StrictMode seinen Zweck. Schalte StrictMode nicht aus, um das Problem zu verbergen. Mach den Effekt sicher für mehrfaches Ausführen, indem du Cleanup hinzufügst, Updates absicherst oder einmalige Initialisierung aus dem Effekt herausnimmst.
Veraltete Closures erzeugen "korrigierende" Updates
Ein häufiges Muster ist ein Effekt, der alten State liest und dann mit setState korrigiert. Diese Korrektur löst einen neuen Render aus, der wiederum eine veraltete Leseoperation erzeugt — und die Schleife beginnt von vorn.
Wenn ein Effekt State verwendet, aber das Dependency-Array nicht stimmt, bekämpfst du womöglich deine eigenen alten Werte. Verwende funktionale Updates, wenn du den aktuellen Wert brauchst.
Memoization kann auch nach hinten losgehen. useCallback/useMemo mit falschen Abhängigkeiten erzeugt trotzdem bei jedem Render neue Funktionen/Objekte. Wenn dieser Wert in einem Dependency-Array landet, läuft dein Effekt jedes Mal.
Schnelle Methoden, um eine noch lebende Schleife zu erkennen:
- Logge "stabile" Abhängigkeiten (Funktionen, Objekte, Arrays) und prüfe, ob sie sich bei jedem Render ändern
- Berechne abgeleitete Objekte innerhalb des Effekts aus primitiven Abhängigkeiten
- Achte darauf, dass jede Subscription/Listener ein Cleanup hat
- Vermeide State-zu-Prop-Synchronisation, es sei denn, du hast einen klaren Grund
Schnelle Checkliste vor dem Release
Mach einen letzten Durchgang mit echtem Nutzerverhalten im Hinterkopf. Schleifen verschwinden oft auf dem Happy Path und kommen zurück, wenn Nutzer schnell klicken, Tabs wechseln oder das Netzwerk langsam ist.
- Suche nach Settern, die während des Renders laufen können (einschließlich Helpers, die aus JSX aufgerufen werden)
- Lies jedes
useEffectwie einen Satz: "Wenn X sich ändert, mache Y." Stelle sicher, dass es eine Stopp-Bedingung gibt - Prüfe die Stabilität der Abhängigkeiten (Inline-Objekte, -Arrays und -Funktionen ändern sich bei jedem Render)
- Verifiziere Cleanup für Timer, Listener, Subscriptions und Observer
- Mache Netzwerk-Arbeit robust: storniere veraltete Requests, dedupe Aufrufe und ignoriere späte Antworten
Ein einfacher Test: öffne die Seite und ändere einen Filter dreimal schnell hintereinander. Wenn du sich überlappende Anfragen siehst und die UI sich weiter "korrigiert", hast du wahrscheinlich instabile Abhängigkeiten und fehlende Abbrüche.
Ein realistisches Beispiel: das Dashboard, das immer wieder neu lädt
Ein häufiger Fall in generierten Admin-Dashboards: eine Liste von Nutzern laden, im State speichern und in einer Tabelle anzeigen. Alles wirkt in Ordnung, außer dass die Seite ständig refetches und die UI ruckelt.
Der Bug
Das Muster beginnt meist mit einem Effekt, der von einem Inline-Objekt abhängt. Dieses Objekt wird bei jedem Render neu erzeugt, also behandelt React es als „geändert“.
// Problem
function AdminUsers({ orgId }) {
const [users, setUsers] = React.useState([]);
const options = { method: "GET", headers: { "x-org": orgId } }; // new each render
React.useEffect(() => {
fetch("/api/users", options)
.then(r => r.json())
.then(data => setUsers(data));
}, [options]);
return <UsersTable users={users} />;
}
Manche Implementierungen verschlimmern das, indem sie die Antwort normalisieren und jedes Mal ein brandneues Array erzeugen, sodass setUsers aufgerufen wird, selbst wenn sich nichts geändert hat.
Die Lösung
Stabilisiere die Effekt-Eingaben, vermeide unnötige Updates und stornieren Sie laufende Anfragen.
function AdminUsers({ orgId }) {
const [users, setUsers] = React.useState([]);
const options = React.useMemo(
() => ({ method: "GET", headers: { "x-org": orgId } }),
[orgId]
);
React.useEffect(() => {
const controller = new AbortController();
fetch("/api/users", { ...options, signal: controller.signal })
.then(r => r.json())
.then(data => {
setUsers(prev => (sameUsers(prev, data) ? prev : data));
})
.catch(err => {
if (err.name !== "AbortError") throw err;
});
return () => controller.abort();
}, [options]);
return <UsersTable users={users} />;
}
Um zu überprüfen, ob es funktioniert hat:
- Füge einen Render-Zähler hinzu und bestätige, dass er nicht weiter ansteigt
- Beobachte die Network-Tab: wiederholte Anfragen sollten aufhören
- Ändere
orgIdeinmal und bestätige, dass du genau einen neuen Fetch bekommst
Ein kleines Refactoring hilft, dass der Fehler nicht zurückkommt: extrahiere den Fetch in einen useUsers(orgId)-Hook, benenne memoized Werte klar und halte Effekt-Abhängigkeiten kurz und stabil.
Nächste Schritte, wenn der Code weiter schleift
Wenn du das offensichtliche Problem (wie ein fehlendes Dependency-Array) behoben hast und die App immer noch dreht, gehe davon aus, dass mehr als ein Auslöser existiert. Viele Schleifen sind eine Kette: ein State-Update löst einen Effekt aus, dieser Effekt ändert etwas anderes und eine andere Komponente reagiert, indem sie zurück auf den ersten State schreibt.
Ein kleiner Fix reicht, wenn du eine klare Ursache findest, wie einen Effekt, der bei jedem Lauf State setzt, oder einen Prop-Callback, dessen Identität sich bei jedem Render ändert.
Eine Umgestaltung ist oft die bessere Wahl, wenn eine Komponente zu viel macht: Fetching, Sortieren, Filtern, Form-State und UI-State sind alle vermischt. Wenn du ständig "nur einmal laufen"-Flags hinzufügst, behandelst du Symptome.
Wenn du ein KI-generiertes React-Codebase geerbt hast, das sich nicht beruhigt, kann FixMyMess (fixmymess.ai) helfen, indem es die Auslöser-Kette verfolgt und den zugrundeliegenden Zustand- und Effektfluss repariert — statt nur Schutzhüllen hinzuzufügen. Oft reicht ein kostenloses Code-Audit, um die genaue Schleife und die schnellste sichere Lösung zu finden.