Skip to content

Commit 228382f

Browse files
authored
fix(angular-query): subscribe to observer outside the Angular zone (#8331)
1 parent 80a49ff commit 228382f

File tree

6 files changed

+98
-74
lines changed

6 files changed

+98
-74
lines changed

packages/angular-query-experimental/src/create-base-query.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -88,23 +88,27 @@ export function createBaseQuery<
8888
)
8989

9090
// observer.trackResult is not used as this optimization is not needed for Angular
91-
const unsubscribe = observer.subscribe(
92-
notifyManager.batchCalls((state: QueryObserverResult<TData, TError>) => {
93-
ngZone.run(() => {
94-
if (
95-
state.isError &&
96-
!state.isFetching &&
97-
// !isRestoring() && // todo: enable when client persistence is implemented
98-
shouldThrowError(observer.options.throwOnError, [
99-
state.error,
100-
observer.getCurrentQuery(),
101-
])
102-
) {
103-
throw state.error
104-
}
105-
resultSignal.set(state)
106-
})
107-
}),
91+
const unsubscribe = ngZone.runOutsideAngular(() =>
92+
observer.subscribe(
93+
notifyManager.batchCalls(
94+
(state: QueryObserverResult<TData, TError>) => {
95+
ngZone.run(() => {
96+
if (
97+
state.isError &&
98+
!state.isFetching &&
99+
// !isRestoring() && // todo: enable when client persistence is implemented
100+
shouldThrowError(observer.options.throwOnError, [
101+
state.error,
102+
observer.getCurrentQuery(),
103+
])
104+
) {
105+
throw state.error
106+
}
107+
resultSignal.set(state)
108+
})
109+
},
110+
),
111+
),
108112
)
109113
destroyRef.onDestroy(unsubscribe)
110114

packages/angular-query-experimental/src/inject-is-fetching.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,19 @@ export function injectIsFetching(
2929

3030
const result = signal(isFetching)
3131

32-
const unsubscribe = cache.subscribe(
33-
notifyManager.batchCalls(() => {
34-
const newIsFetching = queryClient.isFetching(filters)
35-
if (isFetching !== newIsFetching) {
36-
// * and update with each change
37-
isFetching = newIsFetching
38-
ngZone.run(() => {
39-
result.set(isFetching)
40-
})
41-
}
42-
}),
32+
const unsubscribe = ngZone.runOutsideAngular(() =>
33+
cache.subscribe(
34+
notifyManager.batchCalls(() => {
35+
const newIsFetching = queryClient.isFetching(filters)
36+
if (isFetching !== newIsFetching) {
37+
// * and update with each change
38+
isFetching = newIsFetching
39+
ngZone.run(() => {
40+
result.set(isFetching)
41+
})
42+
}
43+
}),
44+
),
4345
)
4446

4547
destroyRef.onDestroy(unsubscribe)

packages/angular-query-experimental/src/inject-is-mutating.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,19 @@ export function injectIsMutating(
2828

2929
const result = signal(isMutating)
3030

31-
const unsubscribe = cache.subscribe(
32-
notifyManager.batchCalls(() => {
33-
const newIsMutating = queryClient.isMutating(filters)
34-
if (isMutating !== newIsMutating) {
35-
// * and update with each change
36-
isMutating = newIsMutating
37-
ngZone.run(() => {
38-
result.set(isMutating)
39-
})
40-
}
41-
}),
31+
const unsubscribe = ngZone.runOutsideAngular(() =>
32+
cache.subscribe(
33+
notifyManager.batchCalls(() => {
34+
const newIsMutating = queryClient.isMutating(filters)
35+
if (isMutating !== newIsMutating) {
36+
// * and update with each change
37+
isMutating = newIsMutating
38+
ngZone.run(() => {
39+
result.set(isMutating)
40+
})
41+
}
42+
}),
43+
),
4244
)
4345

4446
destroyRef.onDestroy(unsubscribe)

packages/angular-query-experimental/src/inject-mutation-state.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,20 @@ export function injectMutationState<TResult = MutationState>(
8080
{ injector },
8181
)
8282

83-
const unsubscribe = mutationCache.subscribe(
84-
notifyManager.batchCalls(() => {
85-
const nextResult = replaceEqualDeep(
86-
result(),
87-
getResult(mutationCache, mutationStateOptionsFn()),
88-
)
89-
if (result() !== nextResult) {
90-
ngZone.run(() => {
91-
result.set(nextResult)
92-
})
93-
}
94-
}),
83+
const unsubscribe = ngZone.runOutsideAngular(() =>
84+
mutationCache.subscribe(
85+
notifyManager.batchCalls(() => {
86+
const nextResult = replaceEqualDeep(
87+
result(),
88+
getResult(mutationCache, mutationStateOptionsFn()),
89+
)
90+
if (result() !== nextResult) {
91+
ngZone.run(() => {
92+
result.set(nextResult)
93+
})
94+
}
95+
}),
96+
),
9597
)
9698

9799
destroyRef.onDestroy(unsubscribe)

packages/angular-query-experimental/src/inject-mutation.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -71,26 +71,30 @@ export function injectMutation<
7171

7272
const result = signal(observer.getCurrentResult())
7373

74-
const unsubscribe = observer.subscribe(
75-
notifyManager.batchCalls(
76-
(
77-
state: MutationObserverResult<
78-
TData,
79-
TError,
80-
TVariables,
81-
TContext
82-
>,
83-
) => {
84-
ngZone.run(() => {
85-
if (
86-
state.isError &&
87-
shouldThrowError(observer.options.throwOnError, [state.error])
88-
) {
89-
throw state.error
90-
}
91-
result.set(state)
92-
})
93-
},
74+
const unsubscribe = ngZone.runOutsideAngular(() =>
75+
observer.subscribe(
76+
notifyManager.batchCalls(
77+
(
78+
state: MutationObserverResult<
79+
TData,
80+
TError,
81+
TVariables,
82+
TContext
83+
>,
84+
) => {
85+
ngZone.run(() => {
86+
if (
87+
state.isError &&
88+
shouldThrowError(observer.options.throwOnError, [
89+
state.error,
90+
])
91+
) {
92+
throw state.error
93+
}
94+
result.set(state)
95+
})
96+
},
97+
),
9498
),
9599
)
96100

packages/angular-query-experimental/src/inject-queries.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ import {
33
QueryClient,
44
notifyManager,
55
} from '@tanstack/query-core'
6-
import { DestroyRef, computed, effect, inject, signal } from '@angular/core'
6+
import {
7+
DestroyRef,
8+
NgZone,
9+
computed,
10+
effect,
11+
inject,
12+
signal,
13+
} from '@angular/core'
714
import { assertInjector } from './util/assert-injector/assert-injector'
815
import type { Injector, Signal } from '@angular/core'
916
import type {
@@ -202,8 +209,9 @@ export function injectQueries<
202209
injector?: Injector,
203210
): Signal<TCombinedResult> {
204211
return assertInjector(injectQueries, injector, () => {
205-
const queryClient = inject(QueryClient)
206212
const destroyRef = inject(DestroyRef)
213+
const ngZone = inject(NgZone)
214+
const queryClient = inject(QueryClient)
207215

208216
const defaultedQueries = computed(() => {
209217
return queries().map((opts) => {
@@ -238,7 +246,9 @@ export function injectQueries<
238246

239247
const result = signal(getCombinedResult() as any)
240248

241-
const unsubscribe = observer.subscribe(notifyManager.batchCalls(result.set))
249+
const unsubscribe = ngZone.runOutsideAngular(() =>
250+
observer.subscribe(notifyManager.batchCalls(result.set)),
251+
)
242252
destroyRef.onDestroy(unsubscribe)
243253

244254
return result

0 commit comments

Comments
 (0)