Signals and the RxJS Interop

Signals and RxJS are not a contradiction but complementary

cover-image
portrait-image

Coming up in version 16 of Angular there are a lot of revolutionary changes, such as signals which will change the way we develop Angular applications from the ground up. They are being described as the new reactive primitive and promise a future without the need of NgZones, such that change detection will be able to run more fine-grained. This will lead to more performant applications and introduces a standard for handling reactivity in Angular.
But hold on a second... What about my beloved RxJS?!


Although both tackle reactivity, signals are meant to be used to correspond to fine-grained value changes introducing a new way of doing change detection more locally, rather than traversing the entire component tree checking for changes. This process is synchronous, which is key deviation from RxJS, cause RxJS is asynchronous. An observable, although reactive, does not have any implications on change detection and is therefore not a reactive primitive. A signal can be thought of as values that change over time for which there is a producer and a consumer. On the other hand, RxJS is an event stream, where an observer can react to events.


By this very definition they are not competitive, but instead complementary to each other. Additionally, the Angular team has put a focus on making the interoperability between signals and RxJS a piece of cake. So don't worry, RxJS is not vanishing. Maybe it will be used just a bit less for cases where it is a bit over-blown, but that is for the better.

Signal to Observable

If you already have a signal and want to create an observable from it which emits whenever the signal produces a new value, than you can use the toObservable function.

readonly searchCriteria = signal('');
readonly result$ = toObservable(this.searchCriteria).pipe(
  filter((criteria) => criteria.length > 4),
  switchMap((criteria) => this.searchService.search(criteria))
);
toObservable

Observable to Signal

If yu want to go the other way around and create a signal from an observable, which produces a new value on each emit of the oservable, than you can use the toSignal function.

readonly searchCriteria = signal('');
readonly result$ = toObservable(this.searchCriteria).pipe(
  filter((criteria) => criteria.length > 4),
  switchMap((criteria) => this.searchService.search(criteria))
);
readonly result = toSignal(this.result$);
toSignal

Attention! Be careful, that the observable emits a value before the signal is read, because otherwise the signal would throw an error, because it needs to have an initial value. You could either make a BehaviorSubject to a signal, or have a startWith , or you specify an initial value in the toSignal function.

readonly result = toSignal(this.result$, { 
  initialValue: [] as Result[] 
});
toSignal - initialValue

Implications

Signals will change a lot in the way we develop Angular applications, including the use of RxJS. RxJS will not go away, but might decline a bit, because there are new ways of doing reactive synchronous programming in Angular that are simply easier in comparison.

One shift we will likely see it the vanishing of the async pipe. In the future whenever an observable would go into the template, there will be a conversion to a signal, such that the fine-grained change detection works.

RxJS will primarily be used in cases where something asynchronous needs to happen on a signal value change. Some Angular APIs will change and provide signals, such as the Router API for accessing route params, which had to be done with a subscribe on an observable before. But others like the HttpClient won't get signal support because the HttpClient by definition is asynchronous and therefore has no need for signals.

GitHub Repository

Comments