11import {
2+ DestroyRef ,
23 Injector ,
34 NgZone ,
45 assertInInjectionContext ,
56 computed ,
6- effect ,
77 inject ,
88 signal ,
99 untracked ,
@@ -17,8 +17,7 @@ import {
1717} from '@tanstack/query-core'
1818import { signalProxy } from './signal-proxy'
1919import { PENDING_TASKS } from './pending-tasks-compat'
20- import type { PendingTaskRef } from './pending-tasks-compat'
21- import type { DefaultError , MutationObserverResult } from '@tanstack/query-core'
20+ import type { DefaultError } from '@tanstack/query-core'
2221import type {
2322 CreateMutateFunction ,
2423 CreateMutationOptions ,
@@ -58,6 +57,7 @@ export function injectMutation<
5857) : CreateMutationResult < TData , TError , TVariables , TOnMutateResult > {
5958 ! options ?. injector && assertInInjectionContext ( injectMutation )
6059 const injector = options ?. injector ?? inject ( Injector )
60+ const destroyRef = injector . get ( DestroyRef )
6161 const ngZone = injector . get ( NgZone )
6262 const pendingTasks = injector . get ( PENDING_TASKS )
6363 const queryClient = injector . get ( QueryClient )
@@ -78,7 +78,15 @@ export function injectMutation<
7878 > | null = null
7979
8080 return computed ( ( ) => {
81- return ( instance ||= new MutationObserver ( queryClient , optionsSignal ( ) ) )
81+ const observerOptions = optionsSignal ( )
82+ return untracked ( ( ) => {
83+ if ( instance ) {
84+ instance . setOptions ( observerOptions )
85+ } else {
86+ instance = new MutationObserver ( queryClient , observerOptions )
87+ }
88+ return instance
89+ } )
8290 } )
8391 } ) ( )
8492
@@ -87,97 +95,74 @@ export function injectMutation<
8795 > ( ( ) => {
8896 const observer = observerSignal ( )
8997 return ( variables , mutateOptions ) => {
90- observer . mutate ( variables , mutateOptions ) . catch ( noop )
98+ void observer . mutate ( variables , mutateOptions ) . catch ( noop )
9199 }
92100 } )
93101
94- /**
95- * Computed signal that gets result from mutation cache based on passed options
96- */
97- const resultFromInitialOptionsSignal = computed ( ( ) => {
98- const observer = observerSignal ( )
99- return observer . getCurrentResult ( )
100- } )
102+ let cleanup : ( ) => void = noop
101103
102104 /**
103- * Signal that contains result set by subscriber
105+ * Returning a writable signal from a computed is similar to `linkedSignal`,
106+ * but compatible with Angular < 19
107+ *
108+ * Compared to `linkedSignal`, this pattern requires extra parentheses:
109+ * - Accessing value: `result()()`
110+ * - Setting value: `result().set(newValue)`
104111 */
105- const resultFromSubscriberSignal = signal < MutationObserverResult <
106- TData ,
107- TError ,
108- TVariables ,
109- TOnMutateResult
110- > | null > ( null )
111-
112- effect (
113- ( ) => {
114- const observer = observerSignal ( )
115- const observerOptions = optionsSignal ( )
112+ const linkedResultSignal = computed ( ( ) => {
113+ const observer = observerSignal ( )
116114
117- untracked ( ( ) => {
118- observer . setOptions ( observerOptions )
119- } )
120- } ,
121- {
122- injector,
123- } ,
124- )
125-
126- effect (
127- ( onCleanup ) => {
128- // observer.trackResult is not used as this optimization is not needed for Angular
129- const observer = observerSignal ( )
130- let pendingTaskRef : PendingTaskRef | null = null
131-
132- untracked ( ( ) => {
133- const unsubscribe = ngZone . runOutsideAngular ( ( ) =>
134- observer . subscribe (
135- notifyManager . batchCalls ( ( state ) => {
136- ngZone . run ( ( ) => {
137- // Track pending task when mutation is pending
138- if ( state . isPending && ! pendingTaskRef ) {
139- pendingTaskRef = pendingTasks . add ( )
140- }
141-
142- // Clear pending task when mutation is no longer pending
143- if ( ! state . isPending && pendingTaskRef ) {
144- pendingTaskRef ( )
145- pendingTaskRef = null
146- }
147-
148- if (
149- state . isError &&
150- shouldThrowError ( observer . options . throwOnError , [ state . error ] )
151- ) {
152- ngZone . onError . emit ( state . error )
153- throw state . error
154- }
155-
156- resultFromSubscriberSignal . set ( state )
157- } )
158- } ) ,
159- ) ,
160- )
161- onCleanup ( ( ) => {
162- // Clean up any pending task on destroy
163- if ( pendingTaskRef ) {
164- pendingTaskRef ( )
165- pendingTaskRef = null
166- }
167- unsubscribe ( )
168- } )
169- } )
170- } ,
171- {
172- injector,
173- } ,
174- )
115+ return untracked ( ( ) => {
116+ const currentResult = observer . getCurrentResult ( )
117+ const result = signal ( currentResult )
118+
119+ cleanup ( )
120+ let pendingTaskRef = currentResult . isPending ? pendingTasks . add ( ) : null
121+
122+ const unsubscribe = ngZone . runOutsideAngular ( ( ) =>
123+ observer . subscribe (
124+ notifyManager . batchCalls ( ( state ) => {
125+ ngZone . run ( ( ) => {
126+ result . set ( state )
127+
128+ // Track pending task when mutation is pending
129+ if ( state . isPending && ! pendingTaskRef ) {
130+ pendingTaskRef = pendingTasks . add ( )
131+ }
132+
133+ // Clear pending task when mutation is no longer pending
134+ if ( ! state . isPending && pendingTaskRef ) {
135+ pendingTaskRef ( )
136+ pendingTaskRef = null
137+ }
138+
139+ if (
140+ state . isError &&
141+ shouldThrowError ( observer . options . throwOnError , [ state . error ] )
142+ ) {
143+ ngZone . onError . emit ( state . error )
144+ throw state . error
145+ }
146+ } )
147+ } ) ,
148+ ) ,
149+ )
150+
151+ cleanup = ( ) => {
152+ // Clean up any pending task on destroy
153+ if ( pendingTaskRef ) {
154+ pendingTaskRef ( )
155+ pendingTaskRef = null
156+ }
157+ unsubscribe ( )
158+ }
159+
160+ return result
161+ } )
162+ } )
175163
176164 const resultSignal = computed ( ( ) => {
177- const resultFromSubscriber = resultFromSubscriberSignal ( )
178- const resultFromInitialOptions = resultFromInitialOptionsSignal ( )
179-
180- const result = resultFromSubscriber ?? resultFromInitialOptions
165+ const result = linkedResultSignal ( ) ( )
181166
182167 return {
183168 ...result ,
@@ -186,6 +171,8 @@ export function injectMutation<
186171 }
187172 } )
188173
174+ destroyRef . onDestroy ( ( ) => cleanup ( ) )
175+
189176 return signalProxy ( resultSignal ) as CreateMutationResult <
190177 TData ,
191178 TError ,
0 commit comments