Die Ausgangslage war ein Feature, in dem sechs Observables über combineLatest
zusammenliefen, um am Ende eine Tabelle zu füllen. Funktioniert hat das. Nur verstand
es niemand mehr, mich eingeschlossen, sobald ich zwei Wochen nicht hineingeschaut
hatte — und zwei Wochen sind in einem Projekt, an dem noch vier andere Leute
arbeiten, keine besonders lange Zeit.
Was sofort besser wurde
Abgeleiteter Zustand liest sich jetzt wie eine Formel und nicht wie eine Pipeline.
Aus einer Kette von map, filter und shareReplay wird ein computed, das man
in einem Atemzug lesen kann:
readonly visibleRows = computed(() => {
const term = this.searchTerm().trim().toLowerCase();
if (!term) return this.rows();
return this.rows().filter((row) => row.title.toLowerCase().includes(term));
});Kein Subscribe, kein Aufräumen, keine async-Pipe im Template. Und der Punkt, der
mir am meisten bringt: Der Wert ist immer da. Ich muss nicht mehr überlegen, ob beim
ersten Rendern schon etwas emittiert wurde.
Signals sind gut im Beschreiben von Zustand. RxJS ist gut im Beschreiben von Ereignissen über Zeit. Die meisten Fehler entstehen da, wo man das eine mit dem anderen erzwingt.
Wo ich bei RxJS geblieben bin
Alles, was mit Zeit zu tun hat: Eingaben entprellen mit debounceTime, Anfragen,
die einander abbrechen sollen, Wiederholversuche mit Backoff. Nachbauen lässt sich
das mit Signals durchaus. Es wird nur länger und schlechter lesbar, weil man von
Hand ausschreibt, was switchMap in einem Wort sagt.
Meine Regel inzwischen: Der Rand der Anwendung spricht RxJS, der Kern spricht
Signals. toSignal ist die Grenze. Die überquert man einmal, nicht fünfmal.
Was ich unterschätzt habe
effect() ist verführerisch und fast immer die falsche Antwort. Dreimal habe ich
einen Effect geschrieben, der einen anderen Signal-Wert setzt. Damit hatte ich mir
genau die Unübersichtlichkeit zurückgeholt, die ich loswerden wollte. Wenn ein Wert
aus anderen Werten folgt, gehört er in ein computed. Ein Effect ist für die
Außenwelt da: Logging, localStorage, ein Canvas neu zeichnen.
Beim nächsten Mal fange ich bei den computed an. Erst danach schaue ich, wo
wirklich noch ein Stream übrig bleibt.