Skip to content

Commit 57251a4

Browse files
authored
feature(angular-query): support providing QueryClient via an InjectionToken (#9128)
This is useful for example in thin app shell applications where feature teams provide their dependencies on lazy loaded route providers while still allowing to easily share a QueryClient. This way the main bundle for the app shell does not include TanStack Query.
1 parent 8f092f9 commit 57251a4

File tree

3 files changed

+107
-10
lines changed

3 files changed

+107
-10
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { TestBed } from '@angular/core/testing'
2+
import { describe, expect, test } from 'vitest'
3+
import {
4+
InjectionToken,
5+
provideExperimentalZonelessChangeDetection,
6+
} from '@angular/core'
7+
import { QueryClient } from '@tanstack/query-core'
8+
import { provideQueryClient } from '../providers'
9+
10+
describe('provideQueryClient', () => {
11+
test('should provide a QueryClient instance directly', () => {
12+
const queryClient = new QueryClient()
13+
14+
TestBed.configureTestingModule({
15+
providers: [
16+
provideExperimentalZonelessChangeDetection(),
17+
provideQueryClient(queryClient),
18+
],
19+
})
20+
21+
const providedQueryClient = TestBed.inject(QueryClient)
22+
expect(providedQueryClient).toBe(queryClient)
23+
})
24+
25+
test('should provide a QueryClient instance using an InjectionToken', () => {
26+
const queryClient = new QueryClient()
27+
const CUSTOM_QUERY_CLIENT = new InjectionToken<QueryClient>('', {
28+
factory: () => queryClient,
29+
})
30+
31+
TestBed.configureTestingModule({
32+
providers: [
33+
provideExperimentalZonelessChangeDetection(),
34+
provideQueryClient(CUSTOM_QUERY_CLIENT),
35+
],
36+
})
37+
38+
const providedQueryClient = TestBed.inject(QueryClient)
39+
expect(providedQueryClient).toBe(queryClient)
40+
})
41+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { TestBed } from '@angular/core/testing'
2+
import { describe, expect, test } from 'vitest'
3+
import {
4+
InjectionToken,
5+
provideExperimentalZonelessChangeDetection,
6+
} from '@angular/core'
7+
import { QueryClient } from '@tanstack/query-core'
8+
import { provideTanStackQuery } from '../providers'
9+
10+
describe('provideTanStackQuery', () => {
11+
test('should provide a QueryClient instance directly', () => {
12+
const queryClient = new QueryClient()
13+
14+
TestBed.configureTestingModule({
15+
providers: [
16+
provideExperimentalZonelessChangeDetection(),
17+
provideTanStackQuery(queryClient),
18+
],
19+
})
20+
21+
const providedQueryClient = TestBed.inject(QueryClient)
22+
expect(providedQueryClient).toBe(queryClient)
23+
})
24+
25+
test('should provide a QueryClient instance using an InjectionToken', () => {
26+
const queryClient = new QueryClient()
27+
const CUSTOM_QUERY_CLIENT = new InjectionToken<QueryClient>('', {
28+
factory: () => queryClient,
29+
})
30+
31+
TestBed.configureTestingModule({
32+
providers: [
33+
provideExperimentalZonelessChangeDetection(),
34+
provideTanStackQuery(CUSTOM_QUERY_CLIENT),
35+
],
36+
})
37+
38+
const providedQueryClient = TestBed.inject(QueryClient)
39+
expect(providedQueryClient).toBe(queryClient)
40+
})
41+
})

packages/angular-query-experimental/src/providers.ts

+25-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
DestroyRef,
33
ENVIRONMENT_INITIALIZER,
4+
InjectionToken,
45
PLATFORM_ID,
56
computed,
67
effect,
@@ -25,10 +26,19 @@ import type {
2526
* for the entire application. You can use `provideQueryClient` to provide a
2627
* different `QueryClient` instance for a part of the application.
2728
* @param queryClient - the `QueryClient` instance to provide.
28-
* @public
29+
* @returns a provider object that can be used to provide the `QueryClient` instance.
2930
*/
30-
export function provideQueryClient(queryClient: QueryClient) {
31-
return { provide: QueryClient, useValue: queryClient }
31+
export function provideQueryClient(
32+
queryClient: QueryClient | InjectionToken<QueryClient>,
33+
): Provider {
34+
return {
35+
provide: QueryClient,
36+
useFactory: () => {
37+
return queryClient instanceof InjectionToken
38+
? inject(queryClient)
39+
: queryClient
40+
},
41+
}
3242
}
3343

3444
/**
@@ -83,15 +93,14 @@ export function provideQueryClient(queryClient: QueryClient) {
8393
* }
8494
* )
8595
* ```
86-
* @param queryClient - A `QueryClient` instance.
96+
* @param queryClient - A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`.
8797
* @param features - Optional features to configure additional Query functionality.
8898
* @returns A set of providers to set up TanStack Query.
89-
* @public
9099
* @see https://tanstack.com/query/v5/docs/framework/angular/quick-start
91100
* @see withDevtools
92101
*/
93102
export function provideTanStackQuery(
94-
queryClient: QueryClient,
103+
queryClient: QueryClient | InjectionToken<QueryClient>,
95104
...features: Array<QueryFeatures>
96105
): EnvironmentProviders {
97106
return makeEnvironmentProviders([
@@ -100,10 +109,16 @@ export function provideTanStackQuery(
100109
// Do not use provideEnvironmentInitializer while Angular < v19 is supported
101110
provide: ENVIRONMENT_INITIALIZER,
102111
multi: true,
103-
useValue: () => {
104-
queryClient.mount()
105-
// Unmount the query client on application destroy
106-
inject(DestroyRef).onDestroy(() => queryClient.unmount())
112+
useFactory: () => {
113+
const client =
114+
queryClient instanceof InjectionToken
115+
? inject(queryClient)
116+
: queryClient
117+
return () => {
118+
client.mount()
119+
// Unmount the query client on application destroy
120+
inject(DestroyRef).onDestroy(() => client.unmount())
121+
}
107122
},
108123
},
109124
features.map((feature) => feature.ɵproviders),

0 commit comments

Comments
 (0)