React-Komponenten verursachen keinen Speicherleck, weil sie häufig gerendert werden. Sie lecken, wenn etwas außerhalb von React weiterläuft, nachdem die Komponente verschwinden sollte. Timer sind eine der einfachsten Möglichkeiten, diesen Fehler zu erzeugen, da setInterval und setTimeout im Browser-Ereignissystem und nicht im Komponentenlebenszyklus von React leben.
Der klassische Timer-Leck
Das sieht harmlos aus:
useEffect(() => {
const id = setInterval(() => {
refreshData();
}, 1000);
}, []);
Wenn die Komponente unmontiert wird, wird das Intervall weiterhin ausgelöst. Das bedeutet, dass der Callback, der erfasste Zustand und jede durch refreshData() ausgelöste Arbeit länger leben kann als beabsichtigt.
Die Behebung ist einfach:
useEffect(() => {
const id = setInterval(() => {
refreshData();
}, 1000);
return () => clearInterval(id);
}, []);
Wo es schlimmer wird
Timer-Lecks werden kostspieliger, wenn der Callback auch Netzwerkoperationen startet:
useEffect(() => {
const id = setInterval(() => {
void refreshData();
}, 5000);
return () => clearInterval(id);
}, [refreshData]);
Jetzt betrifft der Fehler nicht nur den Speicher. Es kann zu doppeltem Polling, verschwendeter CPU und Anfragen kommen, die noch ausgelöst werden, nachdem der Benutzer navigiert hat.
Bessere Ingenieurregel
Jeder Effekt, der eine langlebige Ressource erwirbt, sollte sie im gleichen Effekt freigeben:
- Intervalle
- Timeouts
- Ereignislistener
- Beobachter
- Abonnements
Diese Regel ist einfach, aber sie überführt einen großen Prozentsatz der React-Leak-Fehler. Wenn eine Ressource das Rendern überlebt, sollte sie einen expliziten Aufräumweg haben.
Weiterführende Lektüre