Can't perform React state update on unmounted component: Wie man es behebt und den Code beschleunigt

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Bekannter Bug, der jeden React-Entwickler nervt

\n

Haben Sie jemals in der Browserkonsole die Warnung Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. gesehen? Falls ja – Sie sind nicht allein. Diese Frage auf Stack Overflow hat tausende Aufrufe gesammelt und ist zu einem echten Kopfschmerz für Anfänger und erfahrene React-Entwickler geworden.

\n

In diesem Artikel werden wir verstehen, warum dieser Fehler auftritt, wie man ihn mit TypeScript behebt, und vor allem – wie man die Leistung Ihrer Anwendung optimiert, damit solche Warnungen nicht mehr erscheinen. Los geht's!

\n\n

Warum schreit React wegen einer unmounted component?

\n

Stellen Sie sich vor: Ein Benutzer öffnet eine Seite, startet das Laden von Daten (z.B. über fetch oder axios), wartet aber nicht auf die Antwort und wechselt zu einem anderen Bildschirm. Die Komponente, die die Anfrage gestartet hat, ist bereits demontiert (aus dem DOM entfernt), aber die asynchrone Operation wird noch ausgeführt. Wenn die Antwort eintrifft, versucht der Code, den Zustand (state) einer nicht mehr existierenden Komponente zu aktualisieren. React beschwert sich zu Recht: "Hey, ich kann nichts aktualisieren, was es nicht gibt!".

\n

In TypeScript ist dieses Problem besonders auffällig, wenn Sie strenge Typisierung verwenden und die Cleanup-Funktionen in useEffect vergessen.

\n\n

Klassisches Beispiel des Problems (TypeScript)

\n
import React, { useState, useEffect } from 'react';\n\ninterface User {\n  id: number;\n  name: string;\n}\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n  const [user, setUser] = useState<User | null>(null);\n\n  useEffect(() => {\n    fetch(`/api/users/${userId}`)\n      .then(res => res.json())\n      .then(data => setUser(data)); // Fehler! Komponente könnte demontiert sein\n  }, [userId]);\n\n  return <div>{user?.name}</div>;\n};
\n

Wenn der Benutzer schnell die Route wechselt, wird die Komponente UserProfile entfernt und setUser wird unnötig aufgerufen. Wir erhalten ein Speicherleck und einen Fehler in der Konsole.

\n\n

3 Möglichkeiten, den Fehler zu beheben (mit Beispielen in TypeScript)

\n

Lassen Sie uns drei zuverlässige Ansätze betrachten, die das Problem lösen und die Leistung der React-Anwendung verbessern.

\n\n

Methode 1: Flag mit useRef (am beliebtesten)

\n

Wir erstellen ein ref-Flag, das anzeigt, ob die Komponente montiert ist. Der Zustand wird nur aktualisiert, wenn das Flag true ist.

\n
import React, { useState, useEffect, useRef } from 'react';\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n  const [user, setUser] = useState<User | null>(null);\n  const isMounted = useRef(true);\n\n  useEffect(() => {\n    // Flag bei jeder Montierung zurücksetzen\n    isMounted.current = true;\n\n    fetch(`/api/users/${userId}`)\n      .then(res => res.json())\n      .then(data => {\n        if (isMounted.current) {\n          setUser(data);\n        }\n      });\n\n    // Cleanup: Flag bei Demontierung auf false setzen\n    return () => {\n      isMounted.current = false;\n    };\n  }, [userId]);\n\n  return <div>{user?.name}</div>;\n};
\n

Vorteile: Einfachheit, keine zusätzlichen Bibliotheken erforderlich. Nachteil: Man darf das Cleanup nicht vergessen.

\n\n

Methode 2: AbortController zum Abbrechen der fetch-Anfrage (empfohlen)

\n

Ein moderner und korrekter Ansatz ist es, die Anfrage selbst abzubrechen, anstatt nur die Antwort zu ignorieren. Das spart Server- und Client-Ressourcen.

\n
import React, { useState, useEffect } from 'react';\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n  const [user, setUser] = useState<User | null>(null);\n  const [loading, setLoading] = useState(true);\n\n  useEffect(() => {\n    const controller = new AbortController();\n    const signal = con                

Blogs

Buchempfehlungen