The Art of State: Zustandsmanagement in React-Anwendungen, Teil 1

Wie lassen sich via typischer Szenarien einer React-Anwendung verschiedene Mittel zur Arbeit mit dem Zustand einräumen und was eignet sich für welche Fälle?

In Pocket speichern vorlesen Druckansicht 2 Kommentare lesen
Lesezeit: 18 Min.
Von
  • Nils Hartmann
Inhaltsverzeichnis

Eine zentrales Thema bei der Entwicklung von React-Anwendungen ist die Frage nach dem Verwalten ihres Zustands (State): Wo wird er optimalerweise gehalten, sodass er den Komponenten möglichst effizient zur Verfügung steht? Kann eine Bibliothek für externes State-Management eine Verbesserung sein oder sorgt sie eher für neue Probleme? Dieser Artikel beschreibt verschiedene Arten von Zuständen und stellt die React-eigenen Lösungen zum Arbeiten ihnen vor. In zwei weiteren Teilen werden externe Bibliotheken vorgestellt, die das Arbeiten mit einem globalem Zustand vereinfachen sollen.

The Art of State: Zustandsmanagement in React-Anwendung, die Serie

Der React-Zustand hält die Daten einer Anwendung. Darin kann zum Beispiel hinterlegt sein, welche Daten in einem Eingabefeld eingegeben sind und welche von einem Server gelesen wurden, oder auch UI-Informationen darüber etwa, ob ein Menü geöffnet oder ausgeklappt ist.

Bei Diskussionen über den State wird häufig zwischen lokalem und globalem State unterschieden. Lokaler State ist ein Zustand, den die Komponente direkt vorhält, die ihn auch (ausschließlich) benutzt. Von globalem Zustand ist die Rede, wenn der Zustand für mehrere Teile oder sogar für die ganze Applikation von Bedeutung ist und folglich mehreren Komponenten zur Verfügung stehen muss.

Zur Beantwortung der Frage, welche Technologie zum Einsatz kommt, hilft es, wenn sich Entwickler darüber Gedanken machen, ob ein bestimmter Zustand lokaler oder globaler Natur ist. Sicherlich gibt es zwischen den beiden Extremen Abstufungen, sodass eine Unterscheidung nicht immer nach dem Schwarz-Weiß-Prinzip möglich ist. Zur Diskussion dieser Fragestellung und denkbarer Lösungsansätze soll eine kleine Blog-Anwendung dienen. Der Beispiel-Code, um die Funktionsweise auszuprobieren und nachzuvollziehen, befindet sich auf GitHub.

Die Anwendung stellt auf der ersten Seite eine Vorschau-Liste für die Blog-Posts dar, die ein Server lädt. Das Klicken auf die Vorschau eines Blog-Posts zeigt den kompletten Artikel. Über den Login-Button können sich registrierte Benutzer anmelden und danach eigene Blog-Posts schreiben. Für die beiden Anwendungsfälle gibt es jeweils eigene Formulare.

Die Abbildung 1 zeigt den Editor für einen neuen Blog-Post. Hier können Anwender einen Titel und den eigentlichen Inhalt des Blog-Posts eingeben und ihn speichern. Da der neue Blog-Beitrag, solange er in der Bearbeitung ist, für den Rest der Anwendung irrelevant ist, ist die Komponente mit dem lokalem State implementiert, siehe das folgende Beispiel:

export default function PostEditor() {
  const [title, setTitle] = React.useState("");
  const [body, setBody] = React.useState("");

  // . . .

  return (
    <div>
      <h1>Create Post</h1>

      <label>
        Title
        <input value={title} 
          onChange={(e) => setTitle(e.currentTarget.value)} />
      </label>

      <label>
        Body
        <textarea value={body} 
          onChange={(e) => setBody(e.currentTarget.value)} />
      </label>

      // . . .

    </div>
  );
}

Dasselbe gilt für das Login-Formular. Auch hier spielt für den Rest der Anwendung keine Rolle, was Benutzer als Username beziehungsweise Passwort eingegeben haben. Die Sache sieht anders aus, sobald sie das Login-Formular abgeschlossen haben und der Server ein positives Ergebnis gesendet hat. Die Frage, ob und welcher Benutzer (Name, Rollen etc.) gerade angemeldet ist, spielt für die ganze Anwendung eine Rolle, da davon abhängig die UI bestimmte Elemente anzeigt oder nicht. Es handelt sich um einen "Klassiker" für globalen Zustand.

Der Post-Editor (Abb. 1)

Schwieriger einzuordnen ist die Frage, ob es sich bei der ausgewählten Sortierreihenfolge der Blog-Liste auf der Übersichtsseite um globalen oder lokalen Zustand handelt. Hier können Benutzer aus verschiedenen Kriterien wählen, in welcher Reihenfolge die Posts erscheinen sollen. Auch wenn der Server die eigentliche Sortierung ausführt, sollen Benutzer ihre gewählten Kriterien über einen Seitenwechsel hinweg erhalten. Klicken sie zum Beispiel einen einzelnen Post oder das Login-Formular an und kehren wieder auf die Übersichtsseite zurück, sollen ihre Sortiereinstellungen beibehalten werden. In dem Fall handelt es sich einerseits ("fachlich") um lokalen Zustand, weil die Sortierinformationen nur für die Blog-Listen-Komponente relevant sind. Andererseits können die Informationen nicht direkt an dieser Komponente abgelegt werden, da sie sonst mit dem Verschwinden der Komponente (Wechsel der Seite) ebenfalls verloren gehen. Deswegen sind sie an einem anderen Ort wie einer höheren Komponente abzulegen. Dann lässt sich von globalem Zustand sprechen, der allerdings eher technisch motiviert ist.

Ähnlich ist die Seite zum Darstellen eines einzelnen Blog-Posts zu betrachten. Zwar wird der geladene Post nur für die einzelne Seite benötigt, es wäre allerdings denkbar, dass die einmal geladenen Posts auf dem Client gecacht werden sollen, um eine schnellere Navigation zu ermöglichen. Auch in dem Fall müsste ein eigentlich lokaler State aus seiner "natürlichen" Komponente herauswandern.

Die Einzelansicht eines Blog-Posts zeigt noch eine andere Art von State, der ebenfalls nicht in der Komponente vorgehalten wird: die URL. In ihr ist hinterlegt, welcher Blog-Post gerade dargestellt werden soll (oder welche andere Ansicht). Solche Informationen liegen bei "kleineren" UI-Elementen unter Umständen in einem React State (z. B. ob ein Menü auf- oder zugeklappt ist). Bei "größeren Einheiten", zum Beispiel welche Seite offen ist, lagert man diese Information oft in die URL aus, um die Seiten verlink- und anspringbar zu machen.

Hier gibt es ebenfalls verschiedene Herangehensweisen, welche Informationen die URL und welche React hält. Oftmals ist das eine fachliche Fragestellung: Soll sich eine Seite per URL referenzieren lassen? Sollen Teile der Darstellung ebenfalls per URL gesetzt werden können? Die Sortierkriterien könnten ein Fall für die URL sein, sodass jemand, der einen Link verschickt beziehungsweise empfängt und öffnet, die genau gleiche Darstellung sieht. Neben der URL kann Zustand zum Beispiel in den Speichermöglichkeiten im Browser vorkommen, etwa in Form eines Session Token und als einfache Zwischenspeichermöglichkeit bei der Eingabe neuer Daten in ein Formular.

Zustand in React-Anwendungen lässt sich demnach in global und lokal unterteilen, aber auch nach Funktionen: Geht es etwa um reinen UI State (Menü auf- oder zugeklappt, Sortierkriterien) oder zum Beispiel um das Cachen von Daten? Außerdem unterscheidet sich Zustand in der Art, wie mit ihm interagiert und wie er verändert wird. Daten in einem Eingabefeld verändern sich in der Regel eher schnell, nämlich in der Geschwindigkeit, in der ein Eingabefeld ausgefüllt wird. Ein ausgewähltes Theme oder die Information, ob ein Benutzer eingeloggt ist oder nicht, ändern sich hingegen in der Regel selten. Diese Fragestellungen können Konsequenzen auf die Wahl der "richtigen" Technologie für den jeweiligen Zustand haben, damit man einerseits eine möglichst einfache Herangehensweise wählt, andererseits aber auch eine, die das Problem zum Beispiel hinsichtlich Performance adäquat adressiert.