01 · Problem
Wofür braucht man das?
Direkt geteilter Objektzustand verrät nicht, wer ihn wann verändert hat, und koppelt Leser, Schreiber sowie Seiteneffekte eng aneinander.
02 · Denkmodell
Das mentale Modell
Ein Store besitzt den Zustand. Schreiboperationen erzeugen eine neue Version und informieren nur Abonnenten des geänderten Schlüssels.
Besitzt eigene Zustandskopien mit expliziten Updates und abmeldbaren Subscriptions. Werte müssen per structuredClone kopierbar sein. Verschachtelte Änderungen benötigen set; reservierte Proxy-Namen dürfen keine State-Schlüssel sein.
03 · Kontrollfluss
Was passiert in welcher Reihenfolge?
- Initialzustand defensiv kopieren und nur über eine kleine Store-API zugänglich machen.
- Bei set alten und neuen Wert vergleichen und den Zustand aktualisieren.
- Passende Listener mit Wert, Vorwert und Schlüssel benachrichtigen.
- Eine unsubscribe-Funktion zurückgeben und Listener beim Aufräumen entfernen.
04 · Bauteile
Die entscheidenden Verträge
createStateStore / StateStoreErzeugt einen Store mit eigenem Zustand.set / snapshotAktualisiert explizit und liest unabhängige Kopien.subscribeBeobachtet eine Eigenschaft und liefert eine Abmeldefunktion.05 · Selbst bauen
Selbst implementieren
Eine explizite API ist leichter zu verstehen und zu testen als magische Proxy-Semantik.
- Zustand privat halten und Werte über get auslesen.
- Schreibzugriffe in set bündeln und unveränderte Werte überspringen.
- Subscriptions in Sets verwalten und immer eine unsubscribe-Funktion liefern.
class Store<S extends object> {
private listeners = new Set<(state: Readonly<S>) => void>();
constructor(private state: S) {}
get() { return this.state as Readonly<S>; }
set(update: Partial<S>) {
this.state = { ...this.state, ...update };
for (const listener of this.listeners) listener(this.state);
}
subscribe(listener: (state: Readonly<S>) => void) {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
}06 · Verifizieren
Was solltest du testen?
- set behält unberührte Schlüssel und veröffentlicht den neuen Snapshot.
- unsubscribe verhindert alle späteren Benachrichtigungen.
- Ein Listener darf sich während einer Benachrichtigung sicher abmelden.
Selbst ausprobieren
Formuliere die erste Prüfaussage als fehlschlagenden Test, implementiere die kleinste Korrektur und ergänze einen Grenzfall.
set behält unberührte Schlüssel und veröffentlicht den neuen Snapshot.
Ansatz vergleichen
Dein Test sollte beim gezielten Verletzen der Invariante fehlschlagen und nach der Korrektur wieder bestehen. Halte den Aufbau deterministisch und prüfe ein beobachtbares Ergebnis.
07 · Grenzen
Kompromisse und Stolperfallen
- Snapshots sind nachvollziehbar, kopieren bei großen Objekten jedoch mehr Daten.
- Schlüsselbezogene Listener sparen Arbeit, vergrößern aber die API.
- Proxies wirken bequem, erschweren Debugging und kollidieren leicht mit Store-Methoden.
Werte müssen per structuredClone kopierbar sein. Verschachtelte Änderungen benötigen set; reservierte Proxy-Namen dürfen keine State-Schlüssel sein.
08 · Weiterdenken
Quellcode und verwandte Ideen
yarn tsx packages/state-store/examples/basic.ts