Skip to content

Commit 758414f

Browse files
feat(vue-query): allow options getters in additional composables (#9914)
1 parent 8b38734 commit 758414f

File tree

12 files changed

+270
-39
lines changed

12 files changed

+270
-39
lines changed

.changeset/curvy-webs-create.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/vue-query': minor
3+
---
4+
5+
feat(vue-query): allow options getters in additional composables
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { vi } from 'vitest'
22
import type { Mock } from 'vitest'
33

4-
const { useBaseQuery: originImpl, unrefQueryArgs: originalParse } =
5-
(await vi.importActual('../useBaseQuery')) as any
4+
const { useBaseQuery: originImpl } = (await vi.importActual(
5+
'../useBaseQuery',
6+
)) as any
67

78
export const useBaseQuery: Mock<(...args: Array<any>) => any> =
89
vi.fn(originImpl)
9-
export const unrefQueryArgs = originalParse

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { infiniteQueryOptions } from '../infiniteQueryOptions'
55

66
vi.mock('../useQueryClient')
77

8-
describe('useQuery', () => {
8+
describe('useInfiniteQuery', () => {
99
beforeEach(() => {
1010
vi.useFakeTimers()
1111
})

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

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
2-
import { onScopeDispose, reactive } from 'vue-demi'
2+
import { onScopeDispose, reactive, ref } from 'vue-demi'
33
import { sleep } from '@tanstack/query-test-utils'
44
import { useQuery } from '../useQuery'
55
import { useIsFetching } from '../useIsFetching'
@@ -65,15 +65,10 @@ describe('useIsFetching', () => {
6565
})
6666

6767
test('should properly update filters', async () => {
68-
const filter = reactive({ stale: false })
68+
const filter = reactive({ stale: false, queryKey: ['isFetchingFilter'] })
6969
useQuery({
70-
queryKey: ['isFetching'],
71-
queryFn: () =>
72-
new Promise((resolve) => {
73-
setTimeout(() => {
74-
return resolve('Some data')
75-
}, 100)
76-
}),
70+
queryKey: ['isFetchingFilter'],
71+
queryFn: () => sleep(10).then(() => 'Some data'),
7772
})
7873
const isFetching = useIsFetching(filter)
7974

@@ -84,4 +79,23 @@ describe('useIsFetching', () => {
8479

8580
expect(isFetching.value).toStrictEqual(1)
8681
})
82+
83+
test('should work with options getter and be reactive', async () => {
84+
const staleRef = ref(false)
85+
useQuery({
86+
queryKey: ['isFetchingGetter'],
87+
queryFn: () => sleep(10).then(() => 'Some data'),
88+
})
89+
const isFetching = useIsFetching(() => ({
90+
stale: staleRef.value,
91+
queryKey: ['isFetchingGetter'],
92+
}))
93+
94+
expect(isFetching.value).toStrictEqual(0)
95+
96+
staleRef.value = true
97+
await vi.advanceTimersByTimeAsync(0)
98+
99+
expect(isFetching.value).toStrictEqual(1)
100+
})
87101
})

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

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest'
2-
import { onScopeDispose, reactive } from 'vue-demi'
2+
import { onScopeDispose, reactive, ref } from 'vue-demi'
33
import { sleep } from '@tanstack/query-test-utils'
44
import { useMutation } from '../useMutation'
55
import { useIsMutating, useMutationState } from '../useMutationState'
@@ -88,9 +88,38 @@ describe('useIsMutating', () => {
8888

8989
expect(isMutating.value).toStrictEqual(1)
9090
})
91+
92+
test('should work with options getter and be reactive', async () => {
93+
const keyRef = ref('isMutatingGetter2')
94+
const { mutate } = useMutation({
95+
mutationKey: ['isMutatingGetter'],
96+
mutationFn: (params: string) => sleep(10).then(() => params),
97+
})
98+
mutate('foo')
99+
100+
const isMutating = useIsMutating(() => ({
101+
mutationKey: [keyRef.value],
102+
}))
103+
104+
expect(isMutating.value).toStrictEqual(0)
105+
106+
keyRef.value = 'isMutatingGetter'
107+
108+
await vi.advanceTimersByTimeAsync(0)
109+
110+
expect(isMutating.value).toStrictEqual(1)
111+
})
91112
})
92113

93114
describe('useMutationState', () => {
115+
beforeEach(() => {
116+
vi.useFakeTimers()
117+
})
118+
119+
afterEach(() => {
120+
vi.useRealTimers()
121+
})
122+
94123
it('should return variables after calling mutate 1', () => {
95124
const mutationKey = ['mutation']
96125
const variables = 'foo123'
@@ -127,4 +156,29 @@ describe('useMutationState', () => {
127156

128157
expect(mutationState.value[0]?.variables).toEqual(variables)
129158
})
159+
160+
it('should work with options getter and be reactive', async () => {
161+
const keyRef = ref('useMutationStateGetter2')
162+
const variables = 'foo123'
163+
164+
const { mutate } = useMutation({
165+
mutationKey: ['useMutationStateGetter'],
166+
mutationFn: (params: string) => sleep(10).then(() => params),
167+
})
168+
169+
mutate(variables)
170+
171+
const mutationState = useMutationState(() => ({
172+
filters: { mutationKey: [keyRef.value], status: 'pending' },
173+
select: (mutation) => mutation.state.variables,
174+
}))
175+
176+
expect(mutationState.value).toEqual([])
177+
178+
keyRef.value = 'useMutationStateGetter'
179+
180+
await vi.advanceTimersByTimeAsync(0)
181+
182+
expect(mutationState.value).toEqual([variables])
183+
})
130184
})

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,39 @@ describe('useMutation', () => {
8282
})
8383
})
8484

85+
test('should work with options getter and be reactive', async () => {
86+
const result = 'Mock data'
87+
const keyRef = ref('key01')
88+
const fnMock = vi.fn((params: string) => sleep(10).then(() => params))
89+
const mutation = useMutation(() => ({
90+
mutationKey: [keyRef.value],
91+
mutationFn: fnMock,
92+
}))
93+
94+
mutation.mutate(result)
95+
96+
await vi.advanceTimersByTimeAsync(10)
97+
98+
expect(fnMock).toHaveBeenCalledTimes(1)
99+
expect(fnMock).toHaveBeenNthCalledWith(
100+
1,
101+
result,
102+
expect.objectContaining({ mutationKey: ['key01'] }),
103+
)
104+
105+
keyRef.value = 'key02'
106+
await vi.advanceTimersByTimeAsync(0)
107+
mutation.mutate(result)
108+
await vi.advanceTimersByTimeAsync(10)
109+
110+
expect(fnMock).toHaveBeenCalledTimes(2)
111+
expect(fnMock).toHaveBeenNthCalledWith(
112+
2,
113+
result,
114+
expect.objectContaining({ mutationKey: ['key02'] }),
115+
)
116+
})
117+
85118
test('should update reactive options', async () => {
86119
const queryClient = useQueryClient()
87120
const mutationCache = queryClient.getMutationCache()

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

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ describe('useQueries', () => {
255255
})
256256

257257
test('should be `enabled` to accept getter function', async () => {
258-
const fetchFn = vi.fn()
258+
const fetchFn = vi.fn(() => 'foo')
259259
const checked = ref(false)
260260

261261
useQueries({
@@ -278,7 +278,7 @@ describe('useQueries', () => {
278278
})
279279

280280
test('should allow getters for query keys', async () => {
281-
const fetchFn = vi.fn()
281+
const fetchFn = vi.fn(() => 'foo')
282282
const key1 = ref('key1')
283283
const key2 = ref('key2')
284284

@@ -307,7 +307,7 @@ describe('useQueries', () => {
307307
})
308308

309309
test('should allow arbitrarily nested getters for query keys', async () => {
310-
const fetchFn = vi.fn()
310+
const fetchFn = vi.fn(() => 'foo')
311311
const key1 = ref('key1')
312312
const key2 = ref('key2')
313313
const key3 = ref('key3')
@@ -368,4 +368,67 @@ describe('useQueries', () => {
368368

369369
expect(fetchFn).toHaveBeenCalledTimes(6)
370370
})
371+
372+
test('should work with options getter and be reactive', async () => {
373+
const fetchFn = vi.fn(() => 'foo')
374+
const key1 = ref('key1')
375+
const key2 = ref('key2')
376+
const key3 = ref('key3')
377+
const key4 = ref('key4')
378+
const key5 = ref('key5')
379+
380+
useQueries({
381+
queries: () => [
382+
{
383+
queryKey: [
384+
'key',
385+
key1,
386+
key2.value,
387+
{ key: key3.value },
388+
[{ foo: { bar: key4.value } }],
389+
() => ({
390+
foo: {
391+
bar: {
392+
baz: key5.value,
393+
},
394+
},
395+
}),
396+
],
397+
queryFn: fetchFn,
398+
},
399+
],
400+
})
401+
402+
expect(fetchFn).toHaveBeenCalledTimes(1)
403+
404+
key1.value = 'key1-updated'
405+
406+
await vi.advanceTimersByTimeAsync(0)
407+
408+
expect(fetchFn).toHaveBeenCalledTimes(2)
409+
410+
key2.value = 'key2-updated'
411+
412+
await vi.advanceTimersByTimeAsync(0)
413+
414+
expect(fetchFn).toHaveBeenCalledTimes(3)
415+
416+
key3.value = 'key3-updated'
417+
418+
await vi.advanceTimersByTimeAsync(0)
419+
420+
expect(fetchFn).toHaveBeenCalledTimes(4)
421+
422+
key4.value = 'key4-updated'
423+
424+
await vi.advanceTimersByTimeAsync(0)
425+
426+
expect(fetchFn).toHaveBeenCalledTimes(5)
427+
428+
key5.value = 'key5-updated'
429+
430+
await vi.advanceTimersByTimeAsync(0)
431+
432+
expect(fetchFn).toHaveBeenCalledTimes(6)
433+
})
371434
})

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

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,39 @@ describe('useQuery', () => {
6262
})
6363
})
6464

65+
test('should work with options getter and be reactive', async () => {
66+
const keyRef = ref('key011')
67+
const resultRef = ref('result02')
68+
const query = useQuery(() => ({
69+
queryKey: [keyRef.value],
70+
queryFn: () => sleep(0).then(() => resultRef.value),
71+
}))
72+
73+
await vi.advanceTimersByTimeAsync(0)
74+
75+
expect(query).toMatchObject({
76+
status: { value: 'success' },
77+
data: { value: 'result02' },
78+
isPending: { value: false },
79+
isFetching: { value: false },
80+
isFetched: { value: true },
81+
isSuccess: { value: true },
82+
})
83+
84+
resultRef.value = 'result021'
85+
keyRef.value = 'key012'
86+
await vi.advanceTimersByTimeAsync(0)
87+
88+
expect(query).toMatchObject({
89+
status: { value: 'success' },
90+
data: { value: 'result021' },
91+
isPending: { value: false },
92+
isFetching: { value: false },
93+
isFetched: { value: true },
94+
isSuccess: { value: true },
95+
})
96+
})
97+
6598
test('should return pending status initially', () => {
6699
const query = useQuery({
67100
queryKey: ['key1'],
@@ -274,7 +307,7 @@ describe('useQuery', () => {
274307
})
275308

276309
test('should use the current value for the queryKey when refetch is called', async () => {
277-
const fetchFn = vi.fn()
310+
const fetchFn = vi.fn(() => 'foo')
278311
const keyRef = ref('key11')
279312
const query = useQuery({
280313
queryKey: ['key10', keyRef],
@@ -302,7 +335,7 @@ describe('useQuery', () => {
302335
})
303336

304337
test('should be `enabled` to accept getter function', async () => {
305-
const fetchFn = vi.fn()
338+
const fetchFn = vi.fn(() => 'foo')
306339
const checked = ref(false)
307340

308341
useQuery({
@@ -321,7 +354,7 @@ describe('useQuery', () => {
321354
})
322355

323356
test('should allow getters for query keys', async () => {
324-
const fetchFn = vi.fn()
357+
const fetchFn = vi.fn(() => 'foo')
325358
const key1 = ref('key1')
326359
const key2 = ref('key2')
327360

@@ -346,7 +379,7 @@ describe('useQuery', () => {
346379
})
347380

348381
test('should allow arbitrarily nested getters for query keys', async () => {
349-
const fetchFn = vi.fn()
382+
const fetchFn = vi.fn(() => 'foo')
350383
const key1 = ref('key1')
351384
const key2 = ref('key2')
352385
const key3 = ref('key3')

packages/vue-query/src/useIsFetching.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { getCurrentScope, onScopeDispose, ref, watchEffect } from 'vue-demi'
22
import { useQueryClient } from './useQueryClient'
3+
import { cloneDeepUnref } from './utils'
34
import type { Ref } from 'vue-demi'
45
import type { QueryFilters as QF } from '@tanstack/query-core'
56
import type { MaybeRefDeep } from './types'
67
import type { QueryClient } from './queryClient'
78

8-
export type QueryFilters = MaybeRefDeep<QF>
9+
export type QueryFilters = MaybeRefDeep<QF> | (() => MaybeRefDeep<QF>)
910

1011
export function useIsFetching(
11-
fetchingFilters: MaybeRefDeep<QF> = {},
12+
fetchingFilters: QueryFilters = {},
1213
queryClient?: QueryClient,
1314
): Ref<number> {
1415
if (process.env.NODE_ENV === 'development') {
@@ -24,7 +25,11 @@ export function useIsFetching(
2425
const isFetching = ref()
2526

2627
const listener = () => {
27-
isFetching.value = client.isFetching(fetchingFilters)
28+
const resolvedFilters =
29+
typeof fetchingFilters === 'function'
30+
? fetchingFilters()
31+
: fetchingFilters
32+
isFetching.value = client.isFetching(cloneDeepUnref(resolvedFilters))
2833
}
2934

3035
const unsubscribe = client.getQueryCache().subscribe(listener)

0 commit comments

Comments
 (0)