Wenn man in seiner React App an den Punkt gelangt, an dem das State Management komplexer wird, steht man schnell vor der Wahl einiger Bibliotheken wie react-redux, mobx oder akita.
In diesem Artikel möchte ich eine der State Management Varianten vorstellen: zustand.
Ich persönlich bin seit dem Wechsel von NGXS in Angular zu React ein großer Fan davon. Trotz der geringen Größe (Unter 1kB minified + gezipped) bietet zustand alle Notwendigkeiten für komplexes State Management. Die komplette API basiert auf React Hooks, ist einfach zu nutzen und kann mit eigenen Middlewares angepasst werden.
Nutzung
Als Beispiel habe ich eine kleine React App gebaut, die eine Art „Website-Editor“ darstellen soll: CodeSandbox App
Sie besteht aus den folgenden Bereichen:
- Form: Das Formular um Änderungen einzugeben
- Preview: Eine Live Vorschau der eingegebenen Änderungen
In so einem typischen Szenario wird ein globaler State benötigt, der als „single source of truth“ agiert. So können andere Komponenten sich auf den globalen State verlassen und zusätzlich wird vermieden den State über Props an Komponenten weiterzureichen.
Da in diesem Beispiel zustand als State Management System genutzt wird, kann hier auch auf die Umhüllung von einem Context Provider verzichtet werden.
Um zustand einzusetzen muss in der App ein neuer Store als Hook erstellt werden. In meinem Fall habe ich eine neue Datei erstellt (app.state.js
), die folgende Funktion exportiert.
import create from "zustand";
export const useAppStore = create((set) => ({
title: "MyProduct - 30 day free trial",
description: "Lorem ipsum dolor sit amet...",
setTitle: (title) => set((state) => ({ title })),
setDescription: (description) => set((state) => ({ description }))
}));
Mit import create from "zustand"
wird hier die Funktion zum Erstellen eines Stores von zustand importiert und dann in der Zeile darunter aufgerufen. set
ist eine Funktion, die zustand mitreicht, um den State zu modifizieren. Zusätzlich kann man noch get
hinzufügen, wenn man den gesamten State später benötigt.
In der create
Funktion kann nun der Inhalt des State definiert werden, wie z.B. die einzelnen Properties und Reducer Funktionen, um die Properties zu ändern. Reducer können natürlich auch asynchron sein, zustand ist das vollkommen egal.
Da es sich bei dem Store um eine Hook handelt, kann er auch normal in Komponenten genutzt werden. In der Form.js
Komponente wird zum Beispiel der setTitle
und setDescription
Reducer genutzt, um neue Werte für den Titel und die Beschreibung zu setzen.
const setTitle = useAppStore((state) => state.setTitle);
const setDescription = useAppStore((state) => state.setDescription);
const handleTitleChange = (ev) => {
setTitle(ev.target.value);
};
const handleDescriptionChange = (ev) => {
setDescription(ev.target.value);
};
Erst wird über die vorhin definierte Hook useAppStore
der jeweilige Reducer selektiert. Danach kann dieser mit dem neuen Wert aufgerufen werden.
Um am Schluss die Daten in der Vorschau anzuzeigen, wird genau wie beim Formular die useAppStore
Hook genutzt. Dabei werden statt den Reducern einfach die Properties selektiert.
const title = useAppStore((state) => state.title);
const description = useAppStore((state) => state.description);
Middlewares
Wie bereits angesprochen erlaubt zustand auch die Nutzung von sogenannten Middlewares. Diese liegen als Schichten um den State und erlauben zusätzliche Eingriffe, wie beispielsweise das Loggen von State Änderungen oder das Persistieren des State.
Ein Beispiel ist eine Logging Middleware, die den neuen State als Parameter erhält und in der Konsole ausgibt.
const log = config => (set, get, api) => config(args => {
console.log(" applying", args)
set(args)
console.log(" new state", get())
}, get, api)
Um diese Middleware zu nutzen muss sie danach noch in der create
Funktion aufgerufen werden:
const useAppStore = create(
log(
(set) => ({
…
}),
),
)
Bonus
Wer zustand ohne React nutzen möchte hat Glück, denn zustand stellt auch eine Vanilla JS API bereit, die dann aber ohne Hooks funktioniert.
import create from 'zustand/vanilla'
const store = create(() => ({ ... }))
const { getState, setState, subscribe, destroy } = store
Wer ein Fan von Redux ist, kann zusätzlich noch zustand mit den Redux-Dev-Tools verknüpfen und seine Reducer in Redux Syntax konvertieren. Mehr dazu hier https://github.com/pmndrs/zustand#cant-live-without-redux-like-reducers-and-action-types
Wenn diese Bibliothek dein Interesse geweckt hat und du mehr über zustand erfahren möchtest, empfehle ich die offizielle Dokumentation auf GitHub ⭐
Bild: https://github.com/pmndrs/zustand
Gute Anleitung zum Setup, danke! 🙂