Zum Inhalt springen
diggn.de
← alle Beiträge Angular & TypeScript · · 2 Minuten

Signals statt RxJS — was ich nach drei Monaten anders mache

Ich habe einen mittelgroßen Teil einer App von RxJS-Streams auf Signals umgebaut. Der Code ist kürzer geworden, aber nicht überall besser.

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:

table.component.tsTypeScript
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.

#angular#typescript#signals

Passt dazu