From fc761300fc9d56cab3f5bcc89ecc3047b284d07b Mon Sep 17 00:00:00 2001 From: Arnoud de Vries <6420061+arnoud-dv@users.noreply.github.com> Date: Sat, 23 Nov 2024 15:15:47 +0100 Subject: [PATCH] fix(angular-query): subscribe to observer outside the Angular zone --- .../src/create-base-query.ts | 38 +++++++++------- .../src/inject-is-fetching.ts | 24 +++++----- .../src/inject-is-mutating.ts | 24 +++++----- .../src/inject-mutation-state.ts | 26 ++++++----- .../src/inject-mutation.ts | 44 ++++++++++--------- .../src/inject-queries.ts | 16 +++++-- 6 files changed, 98 insertions(+), 74 deletions(-) diff --git a/packages/angular-query-experimental/src/create-base-query.ts b/packages/angular-query-experimental/src/create-base-query.ts index 7128dc8f8b..b9c8088259 100644 --- a/packages/angular-query-experimental/src/create-base-query.ts +++ b/packages/angular-query-experimental/src/create-base-query.ts @@ -88,23 +88,27 @@ export function createBaseQuery< ) // observer.trackResult is not used as this optimization is not needed for Angular - const unsubscribe = observer.subscribe( - notifyManager.batchCalls((state: QueryObserverResult) => { - ngZone.run(() => { - if ( - state.isError && - !state.isFetching && - // !isRestoring() && // todo: enable when client persistence is implemented - shouldThrowError(observer.options.throwOnError, [ - state.error, - observer.getCurrentQuery(), - ]) - ) { - throw state.error - } - resultSignal.set(state) - }) - }), + const unsubscribe = ngZone.runOutsideAngular(() => + observer.subscribe( + notifyManager.batchCalls( + (state: QueryObserverResult) => { + ngZone.run(() => { + if ( + state.isError && + !state.isFetching && + // !isRestoring() && // todo: enable when client persistence is implemented + shouldThrowError(observer.options.throwOnError, [ + state.error, + observer.getCurrentQuery(), + ]) + ) { + throw state.error + } + resultSignal.set(state) + }) + }, + ), + ), ) destroyRef.onDestroy(unsubscribe) diff --git a/packages/angular-query-experimental/src/inject-is-fetching.ts b/packages/angular-query-experimental/src/inject-is-fetching.ts index 8219897c7d..3c728eea32 100644 --- a/packages/angular-query-experimental/src/inject-is-fetching.ts +++ b/packages/angular-query-experimental/src/inject-is-fetching.ts @@ -29,17 +29,19 @@ export function injectIsFetching( const result = signal(isFetching) - const unsubscribe = cache.subscribe( - notifyManager.batchCalls(() => { - const newIsFetching = queryClient.isFetching(filters) - if (isFetching !== newIsFetching) { - // * and update with each change - isFetching = newIsFetching - ngZone.run(() => { - result.set(isFetching) - }) - } - }), + const unsubscribe = ngZone.runOutsideAngular(() => + cache.subscribe( + notifyManager.batchCalls(() => { + const newIsFetching = queryClient.isFetching(filters) + if (isFetching !== newIsFetching) { + // * and update with each change + isFetching = newIsFetching + ngZone.run(() => { + result.set(isFetching) + }) + } + }), + ), ) destroyRef.onDestroy(unsubscribe) diff --git a/packages/angular-query-experimental/src/inject-is-mutating.ts b/packages/angular-query-experimental/src/inject-is-mutating.ts index 6b0da128c9..867b4ee68d 100644 --- a/packages/angular-query-experimental/src/inject-is-mutating.ts +++ b/packages/angular-query-experimental/src/inject-is-mutating.ts @@ -28,17 +28,19 @@ export function injectIsMutating( const result = signal(isMutating) - const unsubscribe = cache.subscribe( - notifyManager.batchCalls(() => { - const newIsMutating = queryClient.isMutating(filters) - if (isMutating !== newIsMutating) { - // * and update with each change - isMutating = newIsMutating - ngZone.run(() => { - result.set(isMutating) - }) - } - }), + const unsubscribe = ngZone.runOutsideAngular(() => + cache.subscribe( + notifyManager.batchCalls(() => { + const newIsMutating = queryClient.isMutating(filters) + if (isMutating !== newIsMutating) { + // * and update with each change + isMutating = newIsMutating + ngZone.run(() => { + result.set(isMutating) + }) + } + }), + ), ) destroyRef.onDestroy(unsubscribe) diff --git a/packages/angular-query-experimental/src/inject-mutation-state.ts b/packages/angular-query-experimental/src/inject-mutation-state.ts index 39c8da5176..e3a019a0aa 100644 --- a/packages/angular-query-experimental/src/inject-mutation-state.ts +++ b/packages/angular-query-experimental/src/inject-mutation-state.ts @@ -80,18 +80,20 @@ export function injectMutationState( { injector }, ) - const unsubscribe = mutationCache.subscribe( - notifyManager.batchCalls(() => { - const nextResult = replaceEqualDeep( - result(), - getResult(mutationCache, mutationStateOptionsFn()), - ) - if (result() !== nextResult) { - ngZone.run(() => { - result.set(nextResult) - }) - } - }), + const unsubscribe = ngZone.runOutsideAngular(() => + mutationCache.subscribe( + notifyManager.batchCalls(() => { + const nextResult = replaceEqualDeep( + result(), + getResult(mutationCache, mutationStateOptionsFn()), + ) + if (result() !== nextResult) { + ngZone.run(() => { + result.set(nextResult) + }) + } + }), + ), ) destroyRef.onDestroy(unsubscribe) diff --git a/packages/angular-query-experimental/src/inject-mutation.ts b/packages/angular-query-experimental/src/inject-mutation.ts index 9320c7afb2..7112c2ad3e 100644 --- a/packages/angular-query-experimental/src/inject-mutation.ts +++ b/packages/angular-query-experimental/src/inject-mutation.ts @@ -71,26 +71,30 @@ export function injectMutation< const result = signal(observer.getCurrentResult()) - const unsubscribe = observer.subscribe( - notifyManager.batchCalls( - ( - state: MutationObserverResult< - TData, - TError, - TVariables, - TContext - >, - ) => { - ngZone.run(() => { - if ( - state.isError && - shouldThrowError(observer.options.throwOnError, [state.error]) - ) { - throw state.error - } - result.set(state) - }) - }, + const unsubscribe = ngZone.runOutsideAngular(() => + observer.subscribe( + notifyManager.batchCalls( + ( + state: MutationObserverResult< + TData, + TError, + TVariables, + TContext + >, + ) => { + ngZone.run(() => { + if ( + state.isError && + shouldThrowError(observer.options.throwOnError, [ + state.error, + ]) + ) { + throw state.error + } + result.set(state) + }) + }, + ), ), ) diff --git a/packages/angular-query-experimental/src/inject-queries.ts b/packages/angular-query-experimental/src/inject-queries.ts index 423d81c29e..8f969993d6 100644 --- a/packages/angular-query-experimental/src/inject-queries.ts +++ b/packages/angular-query-experimental/src/inject-queries.ts @@ -3,7 +3,14 @@ import { QueryClient, notifyManager, } from '@tanstack/query-core' -import { DestroyRef, computed, effect, inject, signal } from '@angular/core' +import { + DestroyRef, + NgZone, + computed, + effect, + inject, + signal, +} from '@angular/core' import { assertInjector } from './util/assert-injector/assert-injector' import type { Injector, Signal } from '@angular/core' import type { @@ -202,8 +209,9 @@ export function injectQueries< injector?: Injector, ): Signal { return assertInjector(injectQueries, injector, () => { - const queryClient = inject(QueryClient) const destroyRef = inject(DestroyRef) + const ngZone = inject(NgZone) + const queryClient = inject(QueryClient) const defaultedQueries = computed(() => { return queries().map((opts) => { @@ -238,7 +246,9 @@ export function injectQueries< const result = signal(getCombinedResult() as any) - const unsubscribe = observer.subscribe(notifyManager.batchCalls(result.set)) + const unsubscribe = ngZone.runOutsideAngular(() => + observer.subscribe(notifyManager.batchCalls(result.set)), + ) destroyRef.onDestroy(unsubscribe) return result