Skip to content

Commit a0fae50

Browse files
posvaDamianOsipiuk
andauthored
feat(vue-query): support injectable contexts (#5886)
* feat(vue-query): support injectable contexts This feature requires Vue 3.3.0, which has been out for a while now. It allows using vue-query APIs in places where it is valid to use them but currently throws an error. - `hasInjectionContext()`: vuejs/core#8111 - `app.runWithContext()`: vuejs/core#7451 * refactor: add dev warning for effectScope * fix: add memory leak warnings, fix tests * chore: bump vue-demi * chore: bump vue version in integrations --------- Co-authored-by: Damian Osipiuk <[email protected]>
1 parent a496b3d commit a0fae50

File tree

16 files changed

+581
-243
lines changed

16 files changed

+581
-243
lines changed

examples/vue/basic/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
},
1010
"dependencies": {
1111
"@tanstack/vue-query": "^5.0.0-beta.0",
12-
"vue": "^3.2.47"
12+
"vue": "^3.3.0"
1313
},
1414
"devDependencies": {
1515
"@vitejs/plugin-vue": "^4.2.3",

examples/vue/dependent-queries/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
},
1010
"dependencies": {
1111
"@tanstack/vue-query": "^5.0.0-beta.0",
12-
"vue": "^3.2.47"
12+
"vue": "^3.3.0"
1313
},
1414
"devDependencies": {
1515
"@vitejs/plugin-vue": "^4.2.3",

examples/vue/persister/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"@tanstack/query-persist-client-core": "^5.0.0-beta.0",
1212
"@tanstack/query-sync-storage-persister": "^5.0.0-beta.0",
1313
"@tanstack/vue-query": "^5.0.0-beta.0",
14-
"vue": "^3.2.47"
14+
"vue": "^3.3.0"
1515
},
1616
"devDependencies": {
1717
"@vitejs/plugin-vue": "^4.2.3",

integrations/vue-vite/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"dependencies": {
99
"@tanstack/vue-query": "workspace:*",
1010
"@vitejs/plugin-vue": "^4.2.3",
11-
"vue": "^3.2.47",
11+
"vue": "^3.3.0",
1212
"vite": "^4.4.4"
1313
}
1414
}

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,12 @@
8383
"solid-js": "^1.7.8",
8484
"stream-to-array": "^2.3.0",
8585
"tsup": "^7.1.0",
86+
"ts-node": "^10.7.0",
8687
"type-fest": "^3.13.0",
8788
"typescript": "^5.0.4",
8889
"vite": "^4.4.4",
8990
"vitest": "^0.33.0",
90-
"vue": "^3.2.47"
91+
"vue": "^3.3.0"
9192
},
9293
"pnpm": {
9394
"patchedDependencies": {

packages/vue-query/package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,17 @@
6161
"@tanstack/match-sorter-utils": "^8.8.4",
6262
"@tanstack/query-core": "workspace:*",
6363
"@vue/devtools-api": "^6.5.0",
64-
"vue-demi": "^0.13.11"
64+
"vue-demi": "^0.14.6"
6565
},
6666
"devDependencies": {
67-
"@vue/composition-api": "1.7.1",
68-
"vue": "^3.2.47",
67+
"@vue/composition-api": "1.7.2",
68+
"vue": "^3.3.0",
6969
"vue2": "npm:[email protected]",
7070
"vue2.7": "npm:[email protected]"
7171
},
7272
"peerDependencies": {
7373
"@vue/composition-api": "^1.1.2",
74-
"vue": "^2.5.0 || ^3.0.0"
74+
"vue": "^2.6.0 || ^3.3.0"
7575
},
7676
"peerDependenciesMeta": {
7777
"@vue/composition-api": {

packages/vue-query/src/__tests__/useQueryClient.test.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { getCurrentInstance, inject } from 'vue-demi'
1+
import { hasInjectionContext, inject } from 'vue-demi'
22
import { vi } from 'vitest'
33
import { useQueryClient } from '../useQueryClient'
44
import { VUE_QUERY_CLIENT } from '../utils'
55
import type { Mock } from 'vitest'
66

77
describe('useQueryClient', () => {
88
const injectSpy = inject as Mock
9-
const getCurrentInstanceSpy = getCurrentInstance as Mock
9+
const hasInjectionContextSpy = hasInjectionContext as Mock
1010

1111
beforeEach(() => {
1212
vi.restoreAllMocks()
@@ -32,10 +32,10 @@ describe('useQueryClient', () => {
3232
})
3333

3434
test('should throw an error when used outside of setup function', () => {
35-
getCurrentInstanceSpy.mockReturnValueOnce(undefined)
35+
hasInjectionContextSpy.mockReturnValueOnce(false)
3636

3737
expect(useQueryClient).toThrowError()
38-
expect(getCurrentInstanceSpy).toHaveBeenCalledTimes(1)
38+
expect(hasInjectionContextSpy).toHaveBeenCalledTimes(1)
3939
})
4040

4141
test('should call inject with a custom key as a suffix', () => {

packages/vue-query/src/useBaseQuery.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
computed,
3+
getCurrentScope,
34
onScopeDispose,
45
reactive,
56
readonly,
@@ -64,6 +65,14 @@ export function useBaseQuery<
6465
>,
6566
queryClient?: QueryClient,
6667
): UseBaseQueryReturnType<TData, TError> {
68+
if (process.env.NODE_ENV === 'development') {
69+
if (!getCurrentScope()) {
70+
console.warn(
71+
'vue-query composables like "uesQuery()" should only be used inside a "setup()" function or a running effect scope. They might otherwise lead to memory leaks.',
72+
)
73+
}
74+
}
75+
6776
const client = queryClient || useQueryClient()
6877

6978
const defaultedOptions = computed(() => {

packages/vue-query/src/useIsFetching.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { onScopeDispose, ref, watchSyncEffect } from 'vue-demi'
1+
import { getCurrentScope, onScopeDispose, ref, watchSyncEffect } from 'vue-demi'
22
import { useQueryClient } from './useQueryClient'
33
import type { Ref } from 'vue-demi'
44
import type { QueryFilters as QF } from '@tanstack/query-core'
@@ -11,6 +11,14 @@ export function useIsFetching(
1111
fetchingFilters: MaybeRefDeep<QF> = {},
1212
queryClient?: QueryClient,
1313
): Ref<number> {
14+
if (process.env.NODE_ENV === 'development') {
15+
if (!getCurrentScope()) {
16+
console.warn(
17+
'vue-query composables like "uesQuery()" should only be used inside a "setup()" function or a running effect scope. They might otherwise lead to memory leaks.',
18+
)
19+
}
20+
}
21+
1422
const client = queryClient || useQueryClient()
1523

1624
const isFetching = ref()

packages/vue-query/src/useMutation.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
computed,
3+
getCurrentScope,
34
onScopeDispose,
45
reactive,
56
readonly,
@@ -64,6 +65,14 @@ export function useMutation<
6465
>,
6566
queryClient?: QueryClient,
6667
): UseMutationReturnType<TData, TError, TVariables, TContext> {
68+
if (process.env.NODE_ENV === 'development') {
69+
if (!getCurrentScope()) {
70+
console.warn(
71+
'vue-query composables like "uesQuery()" should only be used inside a "setup()" function or a running effect scope. They might otherwise lead to memory leaks.',
72+
)
73+
}
74+
}
75+
6776
const client = queryClient || useQueryClient()
6877
const options = computed(() => {
6978
return client.defaultMutationOptions(cloneDeepUnref(mutationOptions))

packages/vue-query/src/useMutationState.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { computed, onScopeDispose, readonly, ref, watch } from 'vue-demi'
1+
import {
2+
computed,
3+
getCurrentScope,
4+
onScopeDispose,
5+
readonly,
6+
ref,
7+
watch,
8+
} from 'vue-demi'
29
import { useQueryClient } from './useQueryClient'
310
import { cloneDeepUnref } from './utils'
411
import type { DeepReadonly, Ref } from 'vue-demi'
@@ -18,6 +25,14 @@ export function useIsMutating(
1825
filters: MutationFilters = {},
1926
queryClient?: QueryClient,
2027
): Ref<number> {
28+
if (process.env.NODE_ENV === 'development') {
29+
if (!getCurrentScope()) {
30+
console.warn(
31+
'vue-query composables like "uesQuery()" should only be used inside a "setup()" function or a running effect scope. They might otherwise lead to memory leaks.',
32+
)
33+
}
34+
}
35+
2136
const client = queryClient || useQueryClient()
2237
const unreffedFilters = computed(() => ({
2338
...cloneDeepUnref(filters),

packages/vue-query/src/useQueries.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
22
import { QueriesObserver } from '@tanstack/query-core'
3-
import { computed, onScopeDispose, readonly, ref, watch } from 'vue-demi'
3+
import {
4+
computed,
5+
getCurrentScope,
6+
onScopeDispose,
7+
readonly,
8+
ref,
9+
watch,
10+
} from 'vue-demi'
411

512
import { useQueryClient } from './useQueryClient'
613
import { cloneDeepUnref } from './utils'
@@ -167,6 +174,14 @@ export function useQueries<
167174
},
168175
queryClient?: QueryClient,
169176
): Readonly<Ref<TCombinedResult>> {
177+
if (process.env.NODE_ENV === 'development') {
178+
if (!getCurrentScope()) {
179+
console.warn(
180+
'vue-query composables like "uesQuery()" should only be used inside a "setup()" function or a running effect scope. They might otherwise lead to memory leaks.',
181+
)
182+
}
183+
}
184+
170185
const client = queryClient || useQueryClient()
171186

172187
const defaultedQueries = computed(() =>

packages/vue-query/src/useQueryClient.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import { getCurrentInstance, inject } from 'vue-demi'
1+
import { hasInjectionContext, inject } from 'vue-demi'
22

33
import { getClientKey } from './utils'
44
import type { QueryClient } from './queryClient'
55

66
export function useQueryClient(id = ''): QueryClient {
7-
const vm = getCurrentInstance()?.proxy
8-
9-
if (!vm) {
10-
throw new Error('vue-query hooks can only be used inside setup() function.')
7+
// ensures that `inject()` can be used
8+
if (!hasInjectionContext()) {
9+
throw new Error(
10+
'vue-query hooks can only be used inside setup() function or functions that support injection context.',
11+
)
1112
}
1213

1314
const key = getClientKey(id)

packages/vue-query/test-setup.ts

+1-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
import { vi } from 'vitest'
22

3-
import Vue from 'vue2'
4-
Vue.config.productionTip = false
5-
Vue.config.devtools = false
6-
7-
// Hide annoying console warnings for Vue2
8-
import Vue27 from 'vue2.7'
9-
Vue27.config.productionTip = false
10-
Vue27.config.devtools = false
11-
123
vi.mock('vue-demi', async () => {
134
const vue = await vi.importActual('vue-demi')
145
return {
@@ -17,5 +8,6 @@ vi.mock('vue-demi', async () => {
178
provide: vi.fn(),
189
onScopeDispose: vi.fn(),
1910
getCurrentInstance: vi.fn(() => ({ proxy: {} })),
11+
hasInjectionContext: vi.fn(() => true),
2012
}
2113
})

packages/vue-query/vitest.config.ts

+5
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,10 @@ export default defineConfig({
99
globals: true,
1010
setupFiles: ['test-setup.ts'],
1111
coverage: { provider: 'istanbul' },
12+
onConsoleLog: function (log) {
13+
if (log.includes('Download the Vue Devtools extension')) {
14+
return false
15+
}
16+
},
1217
},
1318
})

0 commit comments

Comments
 (0)