The Art of State: Zustandsmanagement in React-Anwendung, Teil 2

Seite 4: Unterschiede zwischen Context und Redux

Inhaltsverzeichnis

Der grundsätzliche Ablauf von Redux ist also: Eine Komponente wählt Daten aus dem globalen Zustand aus und zeigt sie an. Diese oder eine andere Komponente löst eine Aktion aus, der Teile des globalen Zustands verändert. Diejenigen Komponenten, die Daten aus dem Zustand auswählen, der sich jetzt verändert hat, werden neu gerendert. Der grundsätzliche Ablauf ist damit nicht entscheidend anders als oben beim beim Context (mit useReducer) gesehen.

Es gibt aber grundlegende Unterschiede zwischen Redux und den React-Bordmitteln Context und useReducer. Eine Aktion in Redux wird immer an alle Reducer weitergeleitet, das ist in React nicht der Fall. Hier gehört eine Aktion immer zu genau einer Reducer-Funktion. In React ist der Zustand (auch mit useReducer) immer an eine Komponente und deren Lebenszyklus gebunden. Dafür lassen sich beliebig viele Kontexte erzeugen, die zum Beispiel nur einzelnen Teilen der Anwendung zur Verfügung gestellt werden. Der Zustand von Redux ist immer global.

In Redux ist der Store komplett aus den React-Komponenten herausgenommen. Der Store ist gänzlich React-unabhängig und lässt sich auch mit anderen Bibliotheken und Frameworks nutzen. Zum Testen des Stores, der Reducer und Aktionen kann man eine Store-Instanz erzeugen, ohne dass dazu React-Komponenten notwendig sind.

Die Anwendungslogik liegt in Redux zuerst in den Reducer-Funktionen und teilweise auch in den Action-Creator-Funktionen (zum Beispiel für asynchrone Verarbeitung). Beide sind ebenfalls vollständig React-unabhängig und lassen sich somit außerhalb von React testen und in anderen UI-Bibliotheken und Frameworks einsetzen. Mit der Context API ist das nur bei den Reducer-Funktionen der Fall.

Bei Entwicklern ist außerdem das Redux-Tooling beliebt. Redux erlaubt es, alle in einer Anwendung aufgetretenen Aktionen aufzuzeichnen. Mit einer eigenen Browser-Erweiterung, den Redux Devtools, lässt sich der Verlauf der Aktionen einsehen. Das ist praktisch, um nachzuvollziehen, wie sich eine Anwendung verhält. Außerdem erlauben die devtools das sogenannte "time travelling debugging". Darüber können Entwickler in der Zeit hin und her springen. Da Redux – bei Bedarf – alle erzeugten Aktionen und deren Zustandsänderung speichert, kann die Anwendung immer wieder auf einen älteren oder neueren Zustand zurückgesetzt werden, um zum Beispiel Fehler zu suchen. Diese Möglichkeit entfällt bei Context oder useReducer.

Ein weiterer Unterschied ist die Middleware-Funktion von Redux, die sich am Store anmelden und für jede Aktion aufrufen lässt, bevor die Aktion den Reducern übergeben wird. Mit einer Middleware lassen sich somit querschnittliche Aufgaben umsetzen, etwa das zentrale Logging von Aktionen. Das Verarbeiten asynchroner Aktionen ist ebenfalls über eine Middleware implementiert. Ein Pendant hierzu gibt es in den React-eigenen Mitteln nicht.

Redux erfordert eine gewisse Menge an Boilerplate-Code für Reducer, Actions und Action-Creator-Funktionen sowie die Konfiguration des Stores mit den Middlewares. Context beziehungsweise useReducer und vor allem Context mit useState erfordern wesentlich weniger Boilerplate-Code. Allerdings gibt es mit dem Redux Toolkit mittlerweile ein "offizielles" Angebot, das das Arbeiten mit Redux stark vereinfachen soll.

Bestandteil des Toolkits ist einerseits ein Store, der mit häufig verwendeten Middlewares bereits fertig konfiguriert ist. Außerdem enthält es diverse Funktionen, mit denen sich Boilerplate reduzieren lässt, und es verfügt über hervorragenden Support für TypeScript, sodass sich die gesamte Anwendung typsicher implementieren lässt.

Mit dem "RTK Query"-Projekt steht eine (experimentelle) Erweiterung für das Redux Toolkit zur Verfügung, die das Arbeiten mit geladenen Daten im Redux-Umfeld vereinfacht.