# useObservable / toSignal Many existing Angular applications makes use of RxJS / NgRx to manage state. With the planned introduction of signals-based reactivity in Angular we need to make sure that a good RxJS inter-operability story exsists. This document explores possible options of bridging the Observables and signals world - focusing on _consuming_ observables. ## useObservable One idea [we've explored in the past with GDEs](https://angular-team.slack.com/archives/C08M4JKNH/p1663085638927489) is the idea of the `useObservable` utility that could be used as follows: ```typescript! @Component({ selector: 'my-component', template: ` Hello, {{name()}} ` }) class MyComponent { // here names$ is an observable name = useObservable(names$, 'Anonymous'); } ``` Notable points: * the `useObservable` utlity returns a getter function which has the same signature as signals; * the idea _is_ to expose a signal (so observables can participate in computeds etc.) * the default value to `useObservable` assures that a resulting signal always has a value. This propsal has several benefits: * The value is available synchronously in all contexts (TS code and template) with the same API (getter function). * Mitigates well know issues with the `async` pipe approach (effecitivelly eliminating the need for it): * one can use the value in multiple places in the template, without the need for multiple subscriptions w/ multicast, or an outer *ngIf="obs$ | async as obs" alias; * `ngIf`... TODO: list the `async` pipe problems we are solving * Observables are automatically unsubscribed when a component is destroyed, greatly reducing cases where a manual subscription could be the source of memory leaks. Implementation wise, the `useObservable` utilty could be as simple as (pseudo-code): ```typescript! export function useObservable<T>(obs$: Observable<T>, initalValue: T): () => T { const cdRef = inject(ChangeDetectorRef); const valueSignal = createState(initalValue); const unsub = obs$.subscribe((newValue) => { cdRef.markForCheck(); value = newValue; }); // TODO: new Angular API to unsubscribe, ex.: inject(ViewRef).onDestroy(unsub); return () => valueSignal(); } ``` Pros: * very simple utility, well aligned with the longer-term, signal-based strategy * solves number of problems with consuming observables / Cons: * the utility only works in the "injectable context" (component / directive / pipe creation) Requires: * work on the new API to register per-view destroy hooks ## converting observables to signals A different, more generic approach would consist of having a utility function that convers any observable to signal, ex.: ```typescript! const names$ = new BehaviorSubject('Pawel'); export const NAMES_TOKEN = new InjectionToken('Names as signal', { factory() { // I can convert Observable => signal anywhere, not "trapped" in components return toSignal(names$, 'Anonymous') }, }); @Component({ selector: 'my-component', template: ` Hello, {{name()}} ` }) class MyComponent { name = inject(NAMES_TOKEN); } ``` From the user point of view this utility has the similar ergonomics and benefits. Implementation wise, though, things are rather different: ```typescript! export function toObservable<T>(obs$: Observable<T>, initalValue: T): () => T { const valueSignal = createState(initalValue); const unsub = obs$.subscribe((newValue) => { value = newValue; }); // **QUESTION**: who will unsubscribe???? return () => valueSignal(); } ``` **The challange here is the unsubscription**: generally speaking signals have no notation of "cleanup" so it is not obvious where the unsubscribe logic should go. For a general utility like this we might want to introduce the idea of a "cleanup context": * signals, computations and effects need to be registered (created) in a wrapping cleanup context; * signals, computations and effects can register cleanup hooks with the context; * all the registered hooks are executed when context gets destroyed; * cleanup context can be nested; * there is an API to create a top-level cleanup context; While the "cleanup context" idea would cover the use-case in question, it would also add complexity / API surface to the signals library. ## Other considerations * semantics of Observable's emmiting completed / error * "completed" is straighforward: a signal would keep returning the last emitted value * "error" options: * cache error value and keep re-throwing it in a getter <= probably the best option * keep returning the last emitted value (this would effectivelly "swallow" errors) * keep returning some well-known value representing / wrapping the error. * default value: BehaviorSubject (always has value) vs. Observable (might emit asynchronously)