Zustand einordnen

Vier Sorten Zustand, vier verschiedene Werkzeuge

Die Frage lautet selten Redux oder nicht, sondern: Wem gehört dieser Wert, wer ändert ihn, wie lange gilt er und wo muss er nach einem Neuladen wieder herkommen?

KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Seit 1997 am Markt Kleine Gruppen Präsenz und Live-Online Zertifizierte Trainer
Worum es geht

Ein Speicher für alles wird zu dem Teil, den niemand mehr anfassen will

Der Weg dorthin ist immer derselbe. Ein Wert wird an zwei Stellen gebraucht, also wandert er nach oben. Beim nächsten Mal geht es schneller, wenn er gleich in den globalen Speicher kommt. Ein halbes Jahr später liegen dort Serverdaten, Formularinhalte, offene Dialoge, der aktuelle Filter und ein Ladezustand, und jede Änderung an einem Feld betrifft potenziell alles.

Die Folgen sind messbar. Erstens rendert bei jeder Kleinigkeit zu viel, weil die lesenden Komponenten am gesamten Wert hängen statt an einem Ausschnitt. Zweitens stehen Serverdaten doppelt im System, einmal im Speicher und einmal im Zwischenspeicher der Abfragebibliothek, und beide veralten unterschiedlich schnell. Drittens weiß nach einiger Zeit niemand mehr, wer ein Feld ändern darf, weil es keine erkennbare Zuständigkeit gibt.

Der teuerste Einzelfehler ist dabei die vergessene vierte Sorte: Filter, Suchbegriff und Seitenzahl liegen im Speicher statt in der URL. Dann funktioniert der Zurück-Knopf nicht mehr wie erwartet, ein Link auf eine gefilterte Ansicht lässt sich nicht verschicken, und nach dem Neuladen steht die Ansicht wieder auf Anfang. Das fällt in der Entwicklung nie auf und im Alltag jeden Tag.

KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Der Aufbau im Detail

Die vier Sorten in einer Zeile

Diese Zeile ist keine Programmierschnittstelle, sondern eine Merkhilfe für die Aufteilung. Jeder Teil steht für eine Lebensdauer, und Fehler entstehen fast immer dort, wo ein Wert in der falschen Spalte gelandet ist.

Der Aufbau

Zustand = lokal (useState) + geteilt (Context oder Store) + Serverdaten (Cache) + URL (Router)
  1. 01 Es gibt nicht den einen Zustand Zustand

    Der Sammelbegriff verdeckt, dass hier vier Dinge mit verschiedenen Lebensdauern zusammengefasst werden. Wer sie trennt, braucht für die meisten Anwendungen gar keinen globalen Speicher, und wer sie zusammenwirft, bekommt ihn zwangsläufig.

  2. 02 Gehört einer Komponente lokal (useState)

    Der Normalfall und die richtige Voreinstellung. Ein geöffnetes Menü, ein Eingabewert, ein aufgeklappter Abschnitt. Lebensdauer: solange die Komponente eingehängt ist. Halte diesen Zustand so tief wie möglich, das spart nebenbei überflüssige Durchläufe im Baum darüber.

  3. 03 Mehrere Bereiche lesen und schreiben geteilt (Context oder Store)

    Angemeldete Person, Farbschema, Berechtigungen, Inhalt eines Warenkorbs. Bei seltenen Änderungen genügt Context. Bei häufigen Änderungen und vielen lesenden Stellen braucht es einen Speicher, aus dem gezielt einzelne Ausschnitte gelesen werden.

  4. 04 Eine Kopie mit Verfallsdatum Serverdaten (Cache)

    Diese Daten gehören dem Server. Was du brauchst, sind Zwischenspeicher, Ladezustand, Fehlerzustand, Nachladen im Hintergrund und eine Ungültigkeitserklärung nach dem Schreiben. Eine Abfragebibliothek liefert das, ein selbst gebauter Speicher mit useEffect und fetch liefert davon nichts.

  5. 05 Der Zustand, der geteilt werden soll URL (Router)

    Filter, Sortierung, Seitenzahl, Suchbegriff, geöffneter Reiter. Alles, was jemand als Link verschicken oder mit dem Zurück-Knopf zurückholen können soll. Der Router ist hier der Speicher, und Suchparameter sind sein Format.

Wenn es nicht funktioniert

Das siehst du

Ein einzelnes Feld ändert sich und die halbe Anwendung rendert neu.

Warum

Alles liegt in einem Context oder einem Speicher ohne gezieltes Lesen, und jede lesende Komponente hängt am gesamten Wert.

Was hilft

Nach Änderungsrhythmus aufteilen: Was selten wechselt, kommt in einen eigenen Context, was oft wechselt, in einen Speicher, aus dem einzelne Ausschnitte gelesen werden.

Das siehst du

Dieselben Serverdaten stehen an zwei Stellen und widersprechen sich.

Warum

Die Daten werden geholt, in den globalen Speicher kopiert und dort weiterbearbeitet, während der Zwischenspeicher der Abfragebibliothek dieselben Daten unabhängig aktualisiert.

Was hilft

Eine Quelle festlegen. Der Zwischenspeicher der Abfragebibliothek bleibt zuständig, der Speicher hält nur, was wirklich Zustand der Anwendung ist, etwa markierte Zeilen oder ein geöffneter Dialog.

Das siehst du

Nach dem Zurück-Knopf oder einem Neuladen ist der Filter verschwunden.

Warum

Der Filter liegt im Arbeitsspeicher der Anwendung. Für den Browser hat sich nichts geändert, also gibt es nichts, wohin er zurückgehen könnte.

Was hilft

Filter, Sortierung und Seitenzahl in Suchparameter schreiben und aus der URL lesen. Der Router wird damit zur Quelle, und Links auf gefilterte Ansichten funktionieren nebenbei.

Das siehst du

Zwei Komponenten zeigen unterschiedliche Werte für dieselbe Sache.

Warum

Ein Wert wurde mit useState aus einer Eigenschaft heraus initialisiert und lebt seitdem als Kopie weiter. Die Quelle ändert sich, die Kopie nicht.

Was hilft

Nicht kopieren, sondern ableiten. Ein Wert, der sich aus anderen ergibt, wird im Render berechnet. Braucht ein Feld wirklich einen eigenen Anfangswert, setz über key einen bewussten Neuaufbau der Komponente.

Das siehst du

Der Speicher wächst, und niemand weiß mehr, wer welches Feld ändert.

Warum

Es gibt keine Trennung zwischen Zustand und daraus abgeleiteten Werten, und Änderungen passieren verstreut in Komponenten statt über benannte Vorgänge.

Was hilft

Nur das ablegen, was sich nicht berechnen lässt, und Änderungen über benannte Aktionen führen. Ein Reducer oder ein Speicher mit klaren Vorgängen macht die erlaubten Übergänge lesbar und testbar.

Vier Fragen je Wert, und das Werkzeug steht fest

  1. 01 Braucht ihn nur diese Komponente? Dann bleibt er in useState.
  2. 02 Lesen mehrere Bereiche ihn, ändert er sich selten? Dann reicht Context.
  3. 03 Ändert er sich oft und wird an vielen Stellen gelesen? Dann ein Speicher mit Auswahl.
  4. 04 Kommt er vom Server? Dann in den Zwischenspeicher einer Abfragebibliothek.
  5. 05 Soll ein Link ihn transportieren? Dann gehört er in die URL, nicht in den Speicher.
Was du mitnimmst

Was du danach ohne lange Diskussion entscheidest

Statt einer Bibliotheksdiskussion braucht ihr eine Zuordnung: Für jeden Wert wird einmal geklärt, wem er gehört und wie lange er gilt. Danach ergibt sich das Werkzeug fast von selbst, und die Diskussion über Redux oder eine Alternative wird zur Randfrage.

Vier Sorten unterscheiden

Lokaler Zustand einer Komponente, geteilter Zustand mehrerer Bereiche, zwischengespeicherte Serverdaten und Zustand, der in die URL gehört. Die vier haben unterschiedliche Lebensdauern und vertragen deshalb kein gemeinsames Werkzeug.

Serverdaten nicht als Zustand behandeln

Diese Daten gehören nicht deiner Anwendung, sie sind eine Kopie. Eine Abfragebibliothek bringt Zwischenspeicher, Nachladen, Wiederholversuche und Ungültigkeitserklärung mit, und genau das würdest du sonst von Hand in einen Speicher schreiben.

Context als Verteilung lesen, nicht als Speicher

Context reicht einen Wert durch den Baum, ohne ihn über jede Ebene weiterzugeben. Er kennt keine Auswahl von Teilwerten, deshalb rendert bei einer Änderung alles, was liest. Für selten wechselnde Werte ist das ideal, für häufig wechselnde nicht.

Zusammenhängende Übergänge in einen Reducer legen

Wenn drei Felder sich immer gemeinsam ändern, ist useReducer die klarere Form als drei einzelne Zustände. Die erlaubten Übergänge stehen dann an einer Stelle und lassen sich testen, ohne eine Komponente zu rendern.

Die URL als Zustandsspeicher nutzen

Filter, Sortierung, Seitenzahl und die geöffnete Registerkarte gehören in Suchparameter. Damit funktionieren Vor- und Zurück-Knopf, Links lassen sich teilen, und nach dem Neuladen steht die Ansicht wieder da, wo sie war.

Vor der Bibliothek die Pflege prüfen

Schau nach der letzten Veröffentlichung, nach offenen Fehlern und danach, wer das Projekt trägt. Recoil war eine viel benutzte Wahl und wurde Anfang 2025 archiviert, während bestehende Anwendungen weiterliefen und dann umgebaut werden mussten.

KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt

Wann Context nicht mehr trägt

Context löst genau ein Problem: einen Wert durch viele Ebenen zu reichen, ohne ihn überall als Eigenschaft weiterzugeben. Er ist damit eine Verteilung und kein Speicher. Was ihm fehlt, ist die Möglichkeit, nur einen Ausschnitt zu lesen: Jede Komponente, die den Context liest, läuft erneut, sobald sich der Wert ändert, auch wenn sie nur ein einziges Feld daraus benutzt.

Daraus folgen zwei Regeln. Erstens gehört der Wert eines Providers in useMemo, sonst entsteht bei jedem Durchlauf ein neues Objekt und alle lesenden Komponenten laufen mit, ohne dass sich inhaltlich etwas geändert hat. Zweitens lohnt die Aufteilung nach Änderungsrhythmus: Daten in einen Context, die Funktionen zum Ändern in einen zweiten. Komponenten, die nur ändern und nie lesen, hängen dann nicht mehr an den Daten.

Wenn beides nicht mehr reicht, weil sich Werte im Sekundentakt ändern und dutzende Stellen davon lesen, ist der Punkt für einen externen Speicher erreicht. Dessen Kern ist nicht die Verwaltung, sondern die gezielte Auswahl: Eine Komponente meldet an, welchen Ausschnitt sie braucht, und läuft nur, wenn sich genau dieser Ausschnitt ändert. React bringt dafür mit useSyncExternalStore die Anbindung mit, auf der die meisten Bibliotheken aufsetzen.

Serverdaten sind ein Zwischenspeicher, kein Zustand

Ein selbst gebauter Datenabruf besteht schnell aus mehr Teilen als erwartet: Ladezustand, Fehlerzustand, Abbruch bei schnellem Wechsel, Vermeidung doppelter Abfragen derselben Daten, Nachladen beim Zurückkehren zum Fenster, Wiederholversuch nach einem Netzfehler und die Frage, was nach einem Schreibvorgang ungültig wird. Bibliotheken wie TanStack Query oder SWR bringen das mit, und der eigentliche Gewinn ist, dass diese Fälle einheitlich behandelt werden statt in jedem Modul anders.

Der wichtigste Einstellwert ist die Zeit, nach der Daten als veraltet gelten. Steht sie auf null, holt die Anwendung sehr oft nach, was sich wie ein Fehler anfühlt, aber Absicht ist. Bei Stammdaten sind Minuten sinnvoll, bei einer Auftragsliste Sekunden. Der zweite wichtige Punkt ist die Ungültigkeitserklärung nach dem Schreiben: Nach einer erfolgreichen Änderung werden die betroffenen Abfragen als veraltet markiert, statt das Ergebnis von Hand in den Zwischenspeicher zu schreiben.

Wenn ihr mit Server Components oder einem Meta-Framework arbeitet, verschiebt sich ein Teil dieser Aufgabe auf den Server. Dann liegt der Zwischenspeicher dort, und im Browser bleibt weniger übrig. Die Einordnung ändert sich dadurch nicht: Serverdaten bleiben eine Kopie, und die Frage lautet weiterhin, wie lange sie gilt und wodurch sie ungültig wird.

Wenn es doch eine Bibliothek für geteilten Zustand sein soll

Redux Toolkit ist der offizielle Weg, Redux heute einzusetzen: Der alte direkte Aufbau eines Speichers gilt als überholt, stattdessen beschreibst du Ausschnitte mit Zustand und zugehörigen Vorgängen an einer Stelle. Dafür bekommst du Werkzeuge, die in großen Anwendungen zählen: nachvollziehbare Aktionen, Zeitreise in den Entwicklerwerkzeugen, ein etabliertes Muster für Nebenwirkungen und viele Leute, die das Muster kennen. Der Preis ist ein spürbarer Anteil an Gerüst.

Die Bibliothek Zustand, so heißt das Projekt tatsächlich, und Jotai stehen für den anderen Zuschnitt. Zustand ist ein kleiner Speicher, aus dem Komponenten gezielt Ausschnitte lesen, ohne Provider und mit sehr wenig Gerüst. Jotai zerlegt den Zustand stattdessen in kleine Einheiten, die sich zusammensetzen lassen, was gut passt, wenn viele unabhängige Werte im Spiel sind. MobX geht einen dritten Weg über beobachtbare Objekte, was Teams mit objektorientiertem Hintergrund oft entgegenkommt.

Die Auswahl entscheidet ihr an vier Fragen: Wie viel Gerüst verträgt das Team, braucht ihr nachvollziehbare Aktionen für die Fehlersuche, muss der Zustand beim serverseitigen Rendern übertragen werden, und wie gepflegt ist das Projekt? Der letzte Punkt ist kein formaler Haken. Recoil war lange eine verbreitete Antwort auf diese Frage und wurde Anfang 2025 archiviert, sodass Projekte umbauen mussten, ohne dass an ihrer Anwendung etwas falsch gewesen wäre.

Formulare und Aktionen sind ein eigener Fall

Feldinhalte gehören nicht in einen globalen Speicher. Sie ändern sich bei jedem Tastendruck, betreffen genau ein Formular und sind nach dem Absenden erledigt. Wenn sie trotzdem oben landen, rendert die halbe Anwendung beim Tippen mit, und die Prüfung verteilt sich über Ebenen, die mit dem Formular nichts zu tun haben.

React bringt für den Absendevorgang inzwischen eigene Mittel mit. useActionState verbindet ein Formular mit einer Funktion und hält deren Ergebnis fest, useFormStatus liefert den laufenden Zustand an eine Schaltfläche im Formular, und useOptimistic zeigt das erwartete Ergebnis schon an, während der Vorgang noch läuft. Damit fallen genau die drei Zustände weg, die man sonst von Hand pflegt: läuft gerade, ist fehlgeschlagen, wurde erfolgreich übernommen.

Für große Formulare mit Abhängigkeiten zwischen Feldern lohnt eine Formularbibliothek, die die Felder unabhängig voneinander verwaltet. Wichtig bleibt die Zuordnung: Die Prüfung gehört zum Formular, das Ergebnis geht als Vorgang an den Server, und im geteilten Zustand landet höchstens die Rückmeldung darüber, dass etwas gespeichert wurde.

Dazu passende Kurse

Wenn ihr diese Einordnung im Team einüben wollt, könnt ihr bei cmt an einer eigenen Anwendung vier Arten von Zustand auseinanderhalten .

Viele Fehler in dieser Ecke verschwinden, sobald unmögliche Kombinationen gar nicht mehr darstellbar sind, und genau dafür lässt sich Zustandsformen mit Typen absichern .

Wissen prüfen

Wie sicher bist du beim Thema wirklich?

Lesen fühlt sich schnell nach Können an. Ein kurzer Test zeigt dir, was davon schon sitzt und wo sich ein Kurs lohnt. Kostenlos, ohne Anmeldung, mit einer Erklärung zu jeder Antwort.

Der passende Lernpfad

Wenn du nicht nur ein Thema abhaken, sondern eine Rolle ausfüllen willst, zeigt dir der Lernpfad die Kurse in der Reihenfolge, in der sie aufeinander aufbauen.

Karrierepfad

Dein Weg zum React Entwickler

Moderne Web-Produkte werden heute mit React gebaut. Wer es sauber beherrscht, arbeitet an echten Web-Anwendungen – nicht an Spielwiesen.
 

Dieser Karrierepfad führt dich gezielt von den Grundlagen über Architektur und Design bis hin zu professionellen React-Frontends, wie sie in produktiven Teams erwartet werden. Starte einfach durch!

Ein sehr gutes, praxisorientiertes und nachhaltiges Seminar. So sollte es immer sein. Vielen Dank.
LimeSurvey - Anwendertraining Teil 1 (Grundlagen)
Die Schulung ist gerade für Einsteiger oder Entwickler*innen mit eingestaubtem Basiswissen sehr hilfreich.
Vue.js 3 Grundkurs
Ich bin sehr glücklich Franz als Trainer gehabt zu haben. Er hat offensichtlich unglaublich viel Wissen zu den Themen, und schafft es, dieses auch interaktiv und verständlich weiterzugeben.
React Komplettausbildung

Häufige Fragen

Brauchen wir überhaupt noch Redux?
In vielen Anwendungen nicht mehr, weil der größte Teil dessen, was früher im Speicher lag, Serverdaten waren und in einen Zwischenspeicher gehört. Sinnvoll bleibt Redux Toolkit, wenn es viel echten Anwendungszustand mit komplizierten Übergängen gibt, wenn ihr Aktionen für die Fehlersuche nachvollziehen wollt oder wenn mehrere Teams ein gemeinsames Muster brauchen.
Woran merke ich, dass Context nicht mehr reicht?
An zwei Anzeichen. Erstens ändert sich der Wert häufig und im Profiler laufen bei jeder Änderung Komponenten mit, die nur ein anderes Feld daraus lesen. Zweitens wächst der Wert zu einem Objekt mit vielen unabhängigen Teilen, das ihr eigentlich schon in mehrere Contexts aufteilen müsstet. Spätestens dann ist ein Speicher mit gezielter Auswahl die einfachere Lösung.
Wohin gehört der Ladezustand einer Abfrage?
Zur Abfrage selbst und nicht in einen eigenen Zustand. Eine Abfragebibliothek liefert Lade- und Fehlerzustand mit, und sobald du sie zusätzlich in einen Speicher schreibst, hast du zwei Wahrheiten, die auseinanderlaufen können. Wenn du ohne Bibliothek arbeitest, halte beide Werte direkt neben den Daten, für die sie gelten.
Gehört wirklich alles in die URL, was gefiltert wird?
Alles, was jemand teilen, mit dem Zurück-Knopf zurückholen oder nach dem Neuladen wiederfinden können soll. Das sind Filter, Sortierung, Seitenzahl, Suchbegriff und der geöffnete Reiter. Nicht in die URL gehören Feldinhalte während des Tippens, Sitzungsdaten und alles, was vertraulich ist, denn URLs landen in Verläufen, Lesezeichen und Protokollen.
Persönlich für dich da

Deine Ansprechpartner

Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.

Yves Hoppe

Yves Hoppe

Weiterbildung & Beratung

Hilft dir, aus dem Webentwicklung-Programm den passenden Kurs für deinen Stand zu finden.

Norbert Jansen

Norbert Jansen

Beratung & Inhouse

Plant mit dir Inhouse-Trainings, die auf eure Abläufe und euren Datenbestand zugeschnitten sind.

Zustand einordnen, bevor die Bibliothek entschieden wird

Bei cmt baust du diese Aufteilung an einer echten Anwendung auf und siehst, wie viel vom vermeintlichen Zustand am Ende Serverdaten oder Suchparameter sind.