10 React State mit useState

Nachdem wir die Grundlagen von React-Komponenten und die Datenübertragung durch Props kennengelernt haben, stoßen wir unweigerlich auf eine wichtige Erkenntnis: Komponenten benötigen oft die Fähigkeit, sich verändernde Daten zu speichern und darauf zu reagieren. Hier kommt das Konzept des State ins Spiel, das eine der fundamentalen Säulen von React darstellt.

10.1 Was ist State und warum benötigen wir ihn?

State repräsentiert den internen Zustand einer Komponente. Während Props von außen in eine Komponente hereinfließen und unveränderlich sind, gehört State zur Komponente selbst und kann von ihr verändert werden. State ermöglicht es Komponenten, auf Benutzereingaben zu reagieren, Daten zwischen Rendervorgängen zu speichern und die Benutzeroberfläche dynamisch zu aktualisieren.

Stellen Sie sich eine einfache Zähler-Anwendung vor. Ohne State hätten wir keine Möglichkeit, den aktuellen Zählerstand zu speichern. Jedes Mal, wenn der Benutzer auf einen Button klickt, müsste die Information irgendwo gespeichert werden. State bietet genau diese Speichermöglichkeit innerhalb der Komponente.

Der entscheidende Unterschied zu regulären JavaScript-Variablen liegt darin, dass Änderungen am State automatisch ein Re-Rendering der Komponente auslösen. React überwacht State-Änderungen und sorgt dafür, dass die Benutzeroberfläche immer den aktuellen Zustand widerspiegelt.

10.2 Der useState Hook

React stellt uns den useState Hook zur Verfügung, um State in funktionalen Komponenten zu verwalten. Ein Hook ist eine spezielle Funktion, die es ermöglicht, React-Features in funktionalen Komponenten zu nutzen. Der Name useState folgt der Konvention, dass alle Hooks mit “use” beginnen.

Die Syntax von useState ist elegant und folgt einem konsistenten Muster. Der Hook wird mit einem initialen Wert aufgerufen und gibt ein Array mit genau zwei Elementen zurück: dem aktuellen State-Wert und einer Funktion zum Aktualisieren dieses Werts. Durch Array-Destructuring können wir diese beiden Elemente bequem in Variablen extrahieren.

const [count, setCount] = useState(0);

Diese Zeile erstellt eine State-Variable namens count mit dem Initialwert 0 und eine Funktion setCount, mit der wir den Wert später ändern können. Die Namensgebung folgt der Konvention, dass die Setter-Funktion mit “set” beginnt, gefolgt vom Namen der State-Variable.

10.3 Das Re-Rendering-Konzept verstehen

Eines der wichtigsten Konzepte beim Arbeiten mit State ist das Verständnis, wann und wie React Komponenten neu rendert. Jedes Mal, wenn eine State-Variable durch ihre Setter-Funktion geändert wird, plant React ein Re-Rendering der Komponente. Während dieses Re-Renderings wird die gesamte Komponenten-Funktion erneut ausgeführt, aber React behält die State-Werte zwischen den Rendervorgängen bei.

Dieses Verhalten unterscheidet React von traditionellen JavaScript-Anwendungen, wo Sie manuell das DOM manipulieren müssten, um Änderungen sichtbar zu machen. React übernimmt diese Aufgabe automatisch und sorgt dafür, dass die Benutzeroberfläche immer mit dem aktuellen State synchronisiert ist.

Es ist wichtig zu verstehen, dass State-Updates asynchron sind. Wenn Sie setCount(count + 1) aufrufen, wird der neue Wert nicht sofort in der count-Variable verfügbar sein. Stattdessen wird React das Update in die Warteschlange einreihen und beim nächsten Rendering-Zyklus anwenden.

10.4 State richtig aktualisieren

Bei der Aktualisierung von State gibt es einige wichtige Regeln zu beachten. Für primitive Datentypen wie Zahlen, Strings oder Booleans ist die Aktualisierung straightforward - Sie übergeben einfach den neuen Wert an die Setter-Funktion.

Bei komplexeren Datentypen wie Objekten oder Arrays wird es jedoch kritisch. React verwendet einen Vergleich der Referenzen, um zu entscheiden, ob ein Re-Rendering notwendig ist. Das bedeutet, dass Sie niemals bestehende Objekte oder Arrays direkt mutieren dürfen. Stattdessen müssen Sie immer neue Objekte oder Arrays erstellen.

// Richtig: Neues Objekt erstellen
setPerson({ ...person, name: 'Neuer Name' });

// Falsch: Direkte Mutation
person.name = 'Neuer Name';
setPerson(person);

Der Spread-Operator (...) ist ein mächtiges Werkzeug für das Erstellen neuer Objekte mit geänderten Eigenschaften. Er kopiert alle Eigenschaften des ursprünglichen Objekts und überschreibt dann die spezifizierten Eigenschaften.

10.5 Funktionale State-Updates

In bestimmten Situationen reicht es nicht aus, einfach einen neuen Wert zu setzen. Wenn der neue State-Wert vom vorherigen Wert abhängt, sollten Sie funktionale Updates verwenden. Anstatt einen Wert zu übergeben, übergeben Sie eine Funktion, die den vorherigen State als Parameter erhält und den neuen State zurückgibt.

setCount(prevCount => prevCount + 1);

Funktionale Updates sind besonders wichtig, wenn mehrere State-Updates in kurzer Zeit auftreten können oder wenn Sie in Event-Handlers oder anderen asynchronen Kontexten arbeiten. Sie garantieren, dass Sie immer mit dem aktuellsten State-Wert arbeiten.

10.6 Troubleshooting

Ein typischer Fehler besteht darin, State direkt zu mutieren. Dies funktioniert scheinbar bei einfachen Beispielen, führt aber zu schwer debugbaren Problemen, wenn die Anwendung komplexer wird. React erkennt die Änderung nicht und führt kein Re-Rendering durch.

Ein weiterer häufiger Fehler ist die Annahme, dass State-Updates sofort verfügbar sind. Entwickler versuchen manchmal, direkt nach einem setState-Aufruf auf den neuen Wert zuzugreifen, was nicht funktioniert, da Updates asynchron sind.

Auch die Verwechslung zwischen dem State-Wert und der Setter-Funktion kommt vor. Die Setter-Funktion wird verwendet, um State zu ändern, nicht um ihn zu lesen.

10.7 Performance-Überlegungen

State-Updates können Performance-Auswirkungen haben, da sie Re-Renderings auslösen. Bei einfachen Anwendungen ist dies normalerweise kein Problem, aber bei komplexeren Komponenten mit vielen Child-Komponenten können häufige State-Updates zu Performance-Problemen führen.

React ist jedoch hochoptimiert und führt mehrere State-Updates, die in derselben Event-Handler-Funktion auftreten, automatisch zusammen (Batching). Dies reduziert die Anzahl der Re-Renderings und verbessert die Performance.

10.8 Integration mit Event-Handling

State und Event-Handling gehen Hand in Hand. Die meisten State-Änderungen werden durch Benutzerinteraktionen ausgelöst, die über Event-Handler verwaltet werden. Das Kapitel über Event-Handling wird zeigen, wie State-Updates elegant in Response-Funktionen integriert werden können.

Besonders bei Formularelementen wie Input-Feldern ist die Kombination aus State und Event-Handling essentiell. Der State speichert den aktuellen Wert des Input-Felds, während der Event-Handler auf Änderungen reagiert und den State entsprechend aktualisiert.

10.9 Weitere State-Konzepte

Der useState Hook ist nur der Anfang der State-Verwaltung in React. Für komplexere Anwendungen bietet React weitere Hooks wie useReducer für die Verwaltung komplexer State-Logik und useContext für das Teilen von State zwischen Komponenten.

Das Verständnis von useState ist jedoch fundamental für alle weiterführenden State-Konzepte. Die hier gelernten Prinzipien - immutable Updates, das Re-Rendering-Konzept und funktionale Updates - bleiben in allen State-Management-Ansätzen relevant.

Auch externe State-Management-Bibliotheken wie Redux oder Zustand bauen auf den gleichen Grundprinzipien auf, die Sie durch useState lernen. Ein solides Verständnis von React’s eigenem State-Management ist daher die beste Basis für das Erlernen fortgeschrittener Techniken.

10.10 Wann State verwenden?

Die Entscheidung, wann State verwendet werden soll, ist ein wichtiger Teil der Komponentenarchitektur. State sollte verwendet werden, wenn sich Daten über die Zeit ändern können und diese Änderungen in der Benutzeroberfläche sichtbar werden sollen.

Nicht jede Variable in einer Komponente muss State sein. Konstante Werte, berechnete Werte, die von Props oder anderem State abgeleitet werden, und Werte, die sich nicht auf die Darstellung auswirken, sollten als reguläre Variablen behandelt werden.

Das Konzept der “Single Source of Truth” besagt, dass jede Information nur an einer Stelle gespeichert werden sollte. State sollte so strukturiert werden, dass Duplikationen vermieden und die Datenkonsistenz gewährleistet wird.