From 1148b7e0c598d4c004ae37d159b592ba5141c2af Mon Sep 17 00:00:00 2001 From: Matthieu Riegler Date: Tue, 20 May 2025 15:44:09 +0200 Subject: [PATCH] fix(core): unregister `onDestroy` in `toSignal`. (#61514) We cleanup if the observable completes before the onDestroy fires. fixes #61511 PR Close #61514 --- packages/core/rxjs-interop/src/to_signal.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/core/rxjs-interop/src/to_signal.ts b/packages/core/rxjs-interop/src/to_signal.ts index 83e361877d4..ff42e6fadfb 100644 --- a/packages/core/rxjs-interop/src/to_signal.ts +++ b/packages/core/rxjs-interop/src/to_signal.ts @@ -152,6 +152,8 @@ export function toSignal( ); } + let destroyUnregisterFn: (() => void) | undefined; + // Note: This code cannot run inside a reactive context (see assertion above). If we'd support // this, we would subscribe to the observable outside of the current reactive context, avoiding // that side-effect signal reads/writes are attribute to the current consumer. The current @@ -163,6 +165,9 @@ export function toSignal( error: (error) => { state.set({kind: StateKind.Error, error}); }, + complete: () => { + destroyUnregisterFn?.(); + }, // Completion of the Observable is meaningless to the signal. Signals don't have a concept of // "complete". }); @@ -176,7 +181,7 @@ export function toSignal( } // Unsubscribe when the current context is destroyed, if requested. - cleanupRef?.onDestroy(sub.unsubscribe.bind(sub)); + destroyUnregisterFn = cleanupRef?.onDestroy(sub.unsubscribe.bind(sub)); // The actual returned signal is a `computed` of the `State` signal, which maps the various states // to either values or errors.