React ist eine der beliebtesten JavaScript-Bibliotheken für die Erstellung von Benutzeroberflächen. Wenn du diese Benutzeroberflächen baust, musst du möglicherweise Nebeneffekte ausführen, z. B. Daten von einer API abrufen, Ereignisse abonnieren oder das DOM manipulieren.

An dieser Stelle kommt der leistungsstarke useEffect Hook ins Spiel. Er ermöglicht es dir, diese Nebeneffekte deklarativ und effizient zu handhaben und sicherzustellen, dass deine Benutzeroberfläche reaktionsschnell und aktuell bleibt.

Egal, ob du neu in React bist oder ein erfahrener Entwickler, useEffect zu verstehen und zu beherrschen ist unerlässlich, um robuste und dynamische Anwendungen zu erstellen. In diesem Artikel erfährst du, wie der useEffect Hook funktioniert und wie du ihn in deinem React-Projekt einsetzen kannst.

Was ist ein Nebeneffekt in React?

Bei der Arbeit mit React-Komponenten kommt es immer wieder vor, dass wir mit Entitäten interagieren oder Aktionen außerhalb des React-Bereichs durchführen müssen. Diese externen Interaktionen werden als Nebeneffekte bezeichnet.

In React sind die meisten Komponenten reine Funktionen, d. h. sie erhalten Eingaben (props) und produzieren vorhersehbare Ausgaben (JSX), wie im folgenden Beispiel zu sehen:

export default function App() {
  return <User userName="JaneDoe" />   
}
  
function User(props) {
  return <h1>{props.userName}</h1>; // John Doe
}

Die Nebeneffekte sind jedoch nicht vorhersehbar, weil sie Interaktionen außerhalb des üblichen Anwendungsbereichs von React beinhalten.

Nehmen wir ein Beispiel, bei dem du den Titel des Browser-Tabs dynamisch ändern möchtest, um die userName des Nutzers anzuzeigen. Es mag zwar verlockend sein, dies direkt in der Komponente zu tun, aber das ist nicht der empfohlene Ansatz, da dies als Nebeneffekt gilt:

const User = ({ userName }) => {
  document.title = `Hello ${userName}`; // ❌Never do this in the component body — It is a side effect.

  return <h1>{userName}</h1>;
}

Das Ausführen von Nebeneffekten direkt innerhalb des Komponentenkörpers kann den Rendering-Prozess unserer React-Komponente beeinträchtigen.

Um Störungen zu vermeiden, solltest du die Nebeneffekte so abtrennen, dass sie erst gerendert werden oder funktionieren, nachdem unsere Komponente gerendert wurde, um eine klare Trennung zwischen dem Rendering-Prozess und allen notwendigen externen Interaktionen zu gewährleisten. Diese Trennung wird mit dem useEffect Hook vorgenommen.

Verstehen der Grundlagen von useEffect

Der useEffect Hook wurde entwickelt, um Lebenszyklusmethoden wie componentDidMount, componentDidUpdate und componentWillUnmount zu imitieren, die in Klassenkomponenten zu finden sind.

Um useEffect zu verwenden, musst du ihn aus „react“ importieren und ihn dann innerhalb einer Funktionskomponente (auf der obersten Ebene der Komponente) aufrufen. Sie nimmt zwei Argumente entgegen: eine Callback-Funktion und ein optionales Abhängigkeits-Array.

useEffect(callbackFn, [dependencies]);

Dies kann besser geschrieben werden als:

useEffect(() => {
  // code to run when the effect is triggered
}, [dependencies]);
  • Die Callback-Funktion enthält den Code, der ausgeführt wird, wenn die Komponente gerendert wird oder sich der Abhängigkeitswert ändert. Hier führst du den/die Nebeneffekt(e) aus.
  • Das Abhängigkeits-Array gibt die Werte an, die auf Änderungen überwacht werden sollen. Die Callback-Funktion wird ausgeführt, wenn sich ein Wert in diesem Array ändert.

So kannst du zum Beispiel das vorherige Beispiel korrigieren, um den Nebeneffekt in einem useEffect Hook korrekt auszuführen:

import { useEffect } from 'react';

const User = ({ userName }) => {
  useEffect(() => {
    document.title = `Hello ${userName}`;
  }, [userName]);
    
  return <h1>{userName}</h1>;   
}

Im obigen Beispiel wird der useEffect Hook aufgerufen, nachdem die Komponente gerendert wurde und wenn sich die Abhängigkeit – der Wert von userName– ändert.

Arbeiten mit Abhängigkeiten in useEffect

Abhängigkeiten spielen eine entscheidende Rolle bei der Steuerung der Ausführung von useEffect. Sie sind das zweite Argument des useEffect Hooks.

useEffect(() => {
  // code to run when the effect is triggered
}, [dependencies]);

Die Verwendung eines leeren Abhängigkeitsarrays [] stellt sicher, dass der Effekt nur einmal ausgeführt wird, um componentDidMount zu simulieren. Wenn du die Abhängigkeiten richtig angibst, kann der Effekt aktualisiert werden, wenn sich bestimmte Werte ändern, ähnlich wie bei componentDidUpdate.

Hinweis: Beim Umgang mit komplexen Abhängigkeiten solltest du vorsichtig sein. Unnötige Aktualisierungen können vermieden werden, indem du sorgfältig auswählst, welche Werte in das Abhängigkeitsfeld aufgenommen werden sollen.

Wenn du das Abhängigkeitsarray ganz weglässt, wird der Effekt bei jedem Rendern der Komponente ausgeführt, was zu Leistungsproblemen führen kann.

useEffect(() => {
  // code to run when the effect is triggered
});

Wenn du verstehst, wie das Rendering in React funktioniert, ist das ein großer Vorteil, weil du dann weißt, wie wichtig das Abhängigkeitsarray ist.

Wie funktioniert das Rendering in React?

In React erzeugt das Rendering die Benutzeroberfläche (UI) auf der Grundlage des aktuellen Zustands einer Komponente und ihrer Requisiten. Es gibt verschiedene Szenarien, in denen das Rendering stattfindet. Das erste Rendering findet statt, wenn eine Komponente zum ersten Mal gerendert oder montiert wird.

Außerdem löst eine Änderung der state oder props einer Komponente ein erneutes Rendering aus, um sicherzustellen, dass die Benutzeroberfläche die aktualisierten Werte wiedergibt. React-Anwendungen werden mit einer baumartigen Struktur von Komponenten aufgebaut, die eine Hierarchie bilden. React beginnt beim Rendering mit der Wurzelkomponente und rendert die untergeordneten Komponenten rekursiv.

Das bedeutet, dass alle Komponenten gerendert werden, wenn eine Änderung in der Stammkomponente auftritt. Es ist wichtig zu wissen, dass der Aufruf von Nebeneffekten (die meistens teure Funktionen sind) bei jedem Rendering kostspielig sein kann. Um die Leistung zu optimieren, kannst du das Abhängigkeitsarray im useEffect Hook verwenden, um festzulegen, wann er ausgelöst werden soll, und so unnötige Rendervorgänge vermeiden.

Erweiterte Verwendung von useEffect: Aufräumen von Nebeneffekten

Der useEffect Hook ermöglicht die Ausführung von Seiteneffekten und bietet einen Mechanismus zum Aufräumen dieser Seiteneffekte. Dadurch wird sichergestellt, dass alle Ressourcen oder Abonnements, die während des Seiteneffekts erstellt wurden, ordnungsgemäß freigegeben werden, und Speicherlecks werden vermieden.

Sehen wir uns an, wie du mit dem useEffect Hook Nebeneffekte bereinigen kannst:

useEffect(() => {
  // Perform some side effect

  // Cleanup side effect
  return () => {
    // Cleanup tasks
  };
}, []);

Im obigen Codeschnipsel ist die Aufräumfunktion als Rückgabewert innerhalb des useEffect Hooks definiert. Diese Funktion wird aufgerufen, wenn die Komponente demontiert werden soll oder bevor ein erneutes Rendern erfolgt. Sie ermöglicht es dir, alle Ressourcen oder Abonnements, die während des Nebeneffekts erstellt wurden, aufzuräumen.

Hier sind einige Beispiele für die fortgeschrittene Verwendung des useEffect Hooks zum Aufräumen von Nebeneffekten:

1. Intervalle löschen

useEffect(() => {
    const interval = setInterval(() => {
        // Perform some repeated action
    }, 1000);
    return () => {
        clearInterval(interval); // Clean up the interval
    };
}, []);

In diesem Beispiel richten wir ein Intervall ein, das jede Sekunde eine Aktion ausführt. Die Aufräumfunktion löscht das Intervall, damit es nicht weiterläuft, nachdem die Komponente abgehängt wurde.

2. Ereignis-Listener bereinigen

useEffect(() => {
    const handleClick = () => {
        // Handle the click event
    };

    window.addEventListener('click', handleClick);

    return () => {
        window.removeEventListener('click', handleClick); // Clean up the event listener
    };
}, []);

Hier erstellen wir einen Ereignis-Listener für das Klick-Ereignis des Fensterobjekts. Die Aufräumfunktion entfernt den Ereignis-Listener, um Speicherlecks zu vermeiden und eine ordnungsgemäße Bereinigung sicherzustellen.

Die Aufräumfunktion ist optional, aber es wird dringend empfohlen, alle Ressourcen oder Abonnements zu bereinigen, um eine funktionsfähige und effiziente Anwendung zu erhalten.

Verwendung des useEffect Hooks

Mit dem useEffect Hook kannst du Aufgaben ausführen, die eine Interaktion mit externen Entitäten oder APIs erfordern, z. B. Web-APIs wie localStorage oder externe Datenquellen.

Lass uns die Verwendung des useEffect Hooks anhand verschiedener Szenarien untersuchen:

1. Arbeiten mit Web-APIs (localStorage)

useEffect(() => {
 // Storing data in localStorage
  localStorage.setItem('key', 'value');
  // Retrieving data from localStorage
  const data = localStorage.getItem('key');
  // Cleanup: Clearing localStorage when component unmount
  return () => {
    localStorage.removeItem('key');
  };
}, []);

In diesem Beispiel wird der useEffect Hook zum Speichern und Abrufen von Daten aus dem localStorage des Browsers verwendet. Die Aufräumfunktion sorgt dafür, dass der localStorage gelöscht wird, wenn die Komponente nicht mehr eingebunden ist (dies ist nicht immer ein guter Anwendungsfall, da du die localStorage-Daten vielleicht behalten möchtest, bis der Browser aktualisiert wird).

2. Daten von einer externen API abrufen

useEffect(() => {
  // Fetching data from an external API
  fetch('https://api.example.com/data')
    .then((response) => response.json())
    .then((data) => {
      // Do something with the data
    });
}, []);

Hier wird der useEffect Hook verwendet, um Daten von einer externen API abzurufen. Die abgerufenen Daten können dann innerhalb der Komponente verarbeitet und verwendet werden. Es ist nicht zwingend erforderlich, immer eine Aufräumfunktion hinzuzufügen.

Andere beliebte Nebeneffekte

Der useEffect Hook kann für verschiedene andere Nebeneffekte verwendet werden, wie z. B.:

A. Das Abonnieren von Ereignissen:

useEffect(() => {
  window.addEventListener('scroll', handleScroll);
  return () => {
    window.removeEventListener('scroll', handleScroll);
  };
}, []);

B. Ändern des Dokumententitels:

useEffect(() => {
  document.title = 'New Title';
  return () => {
    document.title = 'Previous Title';
  };
}, []);

C. Timer verwalten:

useEffect(() => {
  const timer = setInterval(() => {
    // Do something repeatedly
  }, 1000);
  return () => {
    clearInterval(timer);
  };
}, []);

Häufige useEffect-Fehler und wie du sie vermeidest

Bei der Arbeit mit dem useEffect Hook in React kann es zu Fehlern kommen, die zu unerwartetem Verhalten oder Leistungsproblemen führen können.

Wenn du diese Fehler verstehst und weißt, wie du sie vermeiden kannst, kannst du eine reibungslose und fehlerfreie Nutzung von useEffect sicherstellen.

Sehen wir uns einige häufige useEffect Fehler und ihre Lösungen an:

1. Fehlendes Abhängigkeits-Array

Ein häufiger Fehler ist es, zu vergessen, ein Abhängigkeits-Array als zweites Argument des useEffect Hooks anzugeben.

ESLint zeigt dies immer als Warnung an, weil es zu unbeabsichtigtem Verhalten führen kann, z. B. zu übermäßigem Re-Rendering oder veralteten Daten.

useEffect(() => {
  // Side effect code
}); // Missing dependency array

Lösung: Gib immer ein Abhängigkeits-Array an useEffect weiter, auch wenn es leer ist. Füge alle Variablen oder Werte ein, von denen der Effekt abhängt. So kann React feststellen, wann der Effekt ausgeführt oder übersprungen werden soll.

useEffect(() => {
  // Side effect code
}, []); // Empty dependency array or with appropriate dependencies

2. Falsches Abhängigkeits-Array

Die Angabe eines falschen Abhängigkeitsarrays kann ebenfalls zu Problemen führen. Wenn das Abhängigkeitsarray nicht genau definiert ist, wird der Effekt möglicherweise nicht ausgeführt, wenn sich die erwarteten Abhängigkeiten ändern.

const count = 5;
const counter = 0;
useEffect(() => {
  // Side effect code that depends on 'count'
  let answer = count + 15;
}, [count]); // Incorrect dependency array

Lösung: Achte darauf, alle notwendigen Abhängigkeiten in das Abhängigkeitsfeld aufzunehmen. Wenn der Effekt von mehreren Variablen abhängt, nimm alle auf, um den Effekt auszulösen, wenn sich eine der Abhängigkeiten ändert.

const count = 5;
useEffect(() => {
  // Side effect code that depends on 'count'
  let answer = count + 15;
}, [count]); // Correct dependency array

3. Endlosschleifen

Eine Endlosschleife kann entstehen, wenn der Effekt einen Zustand oder eine Requisite ändert, die auch vom Effekt selbst abhängig ist. Das führt dazu, dass der Effekt immer wieder ausgelöst wird, was zu einer übermäßigen Neudarstellung führt und die Anwendung möglicherweise einfriert.

const [count, setCount] = useState(0);
useEffect(() => {
  setCount(count + 1); // Modifying the dependency 'count' inside the effect
}, [count]); // Dependency array includes 'count'

Lösung: Stelle sicher, dass der Effekt nicht direkt eine Abhängigkeit ändert, die in seinem Abhängigkeitsfeld enthalten ist. Erstelle stattdessen separate Variablen oder verwende andere Techniken zur Zustandsverwaltung, um die notwendigen Änderungen durchzuführen.

const [count, setCount] = useState(0);
useEffect(() => {
  setCount((prevCount) => prevCount + 1); // Modifying the 'count' using a callback
}, []); // You can safely remove the 'count' dependency

4. Vergessen Aufzuräumen

Wenn du die Bereinigung von Nebeneffekten vernachlässigst, kann das zu Speicherlecks oder unnötigem Ressourcenverbrauch führen. Das Nichtbereinigen von Ereignis-Listenern, Intervallen oder Abonnements kann zu unerwartetem Verhalten führen, vor allem, wenn die Komponente abgemeldet wird.

useEffect(() => {
  const timer = setInterval(() => {
    // Perform some action repeatedly
  }, 1000);
  // Missing cleanup
  return () => {
    clearInterval(timer); // Cleanup missing in the return statement
  };
}, []);

Lösung: Biete immer eine Aufräumfunktion in der Rückgabeanweisung des useEffect Hooks an.

useEffect(() => {
  const timer = setInterval(() => {
    // Perform some action repeatedly
  }, 1000);
  return () => {
    clearInterval(timer); // Cleanup included in the return statement
  };
}, []);

Wenn du dir dieser häufigen useEffect Fehler bewusst bist und die empfohlenen Lösungen befolgst, kannst du mögliche Fallstricke vermeiden und die korrekte und effiziente Verwendung des useEffect Hooks in deinen React-Anwendungen sicherstellen.

Zusammenfassung

Der useEffect Hook von React ist ein mächtiges Werkzeug, um Nebeneffekte in Funktionskomponenten zu verwalten. Jetzt, da du ein tieferes Verständnis von useEffect hast, ist es an der Zeit, dein Wissen anzuwenden und deine React-Anwendungen zum Leben zu erwecken.

Du kannst deine React-Anwendung auch live laufen lassen, indem du sie kostenlos auf dem Anwendungs-Hosting von Kinsta bereitstellst!

Jetzt bist du dran. Was denkst du über den useEffect Hook? Teile sie uns in den Kommentaren unten mit.