Skip to content

Commit b97513f

Browse files
thalassophiliaDamianOsipiukautofix-ci[bot]
authored
feat(vue-query): add usePrefetchQuery and usePrefetchInfiniteQuery (#10372)
* feat(vue-query): add prefetch composables - Add usePrefetchQuery and usePrefetchInfiniteQuery to vue-query. - Includes runtime and type tests, plus a changeset * docs(vue-query): document prefetch composables * revert unrelated change * ci: apply automated fixes --------- Co-authored-by: Damian Osipiuk <osipiukd+git@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 0c2bf1e commit b97513f

File tree

11 files changed

+658
-0
lines changed

11 files changed

+658
-0
lines changed

.changeset/puny-poems-tell.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+
Add usePrefetchQuery and usePrefetchInfiniteQuery to vue-query.

docs/config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,14 @@
10671067
"label": "infiniteQueryOptions",
10681068
"to": "framework/vue/reference/infiniteQueryOptions"
10691069
},
1070+
{
1071+
"label": "usePrefetchQuery",
1072+
"to": "framework/vue/reference/usePrefetchQuery"
1073+
},
1074+
{
1075+
"label": "usePrefetchInfiniteQuery",
1076+
"to": "framework/vue/reference/usePrefetchInfiniteQuery"
1077+
},
10701078
{
10711079
"label": "hydration",
10721080
"to": "framework/vue/reference/hydration"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
id: usePrefetchInfiniteQuery
3+
title: usePrefetchInfiniteQuery
4+
ref: docs/framework/react/reference/usePrefetchInfiniteQuery.md
5+
replace: { '@tanstack/react-query': '@tanstack/vue-query' }
6+
---
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
id: usePrefetchQuery
3+
title: usePrefetchQuery
4+
ref: docs/framework/react/reference/usePrefetchQuery.md
5+
replace: { '@tanstack/react-query': '@tanstack/vue-query' }
6+
---
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { assertType, describe, expectTypeOf, it } from 'vitest'
2+
import { ref } from 'vue-demi'
3+
import { skipToken } from '@tanstack/query-core'
4+
import { usePrefetchInfiniteQuery } from '..'
5+
6+
describe('usePrefetchInfiniteQuery', () => {
7+
it('should return nothing', () => {
8+
const result = usePrefetchInfiniteQuery({
9+
queryKey: ['key'],
10+
queryFn: () => Promise.resolve(5),
11+
initialPageParam: 1,
12+
getNextPageParam: () => 1,
13+
})
14+
15+
expectTypeOf(result).toEqualTypeOf<void>()
16+
})
17+
18+
it('should require initialPageParam and getNextPageParam', () => {
19+
assertType(
20+
// @ts-expect-error TS2345
21+
usePrefetchInfiniteQuery({
22+
queryKey: ['key'],
23+
queryFn: () => Promise.resolve(5),
24+
}),
25+
)
26+
})
27+
28+
it('should not allow refetchInterval, enabled or throwOnError options', () => {
29+
assertType(
30+
usePrefetchInfiniteQuery({
31+
queryKey: ['key'],
32+
queryFn: () => Promise.resolve(5),
33+
initialPageParam: 1,
34+
getNextPageParam: () => 1,
35+
// @ts-expect-error TS2353
36+
refetchInterval: 1000,
37+
}),
38+
)
39+
40+
assertType(
41+
usePrefetchInfiniteQuery({
42+
queryKey: ['key'],
43+
queryFn: () => Promise.resolve(5),
44+
initialPageParam: 1,
45+
getNextPageParam: () => 1,
46+
// @ts-expect-error TS2353
47+
enabled: true,
48+
}),
49+
)
50+
51+
assertType(
52+
usePrefetchInfiniteQuery({
53+
queryKey: ['key'],
54+
queryFn: () => Promise.resolve(5),
55+
initialPageParam: 1,
56+
getNextPageParam: () => 1,
57+
// @ts-expect-error TS2353
58+
throwOnError: true,
59+
}),
60+
)
61+
})
62+
63+
it('should accept refs in infinite query options', () => {
64+
assertType(
65+
usePrefetchInfiniteQuery({
66+
queryKey: ['key', ref('id')],
67+
queryFn: () => Promise.resolve(5),
68+
initialPageParam: ref(1),
69+
getNextPageParam: () => 1,
70+
staleTime: ref(1000),
71+
}),
72+
)
73+
})
74+
75+
it('should not allow skipToken in queryFn', () => {
76+
assertType(
77+
usePrefetchInfiniteQuery({
78+
queryKey: ['key'],
79+
initialPageParam: 1,
80+
getNextPageParam: () => 1,
81+
// @ts-expect-error
82+
queryFn: skipToken,
83+
}),
84+
)
85+
86+
assertType(
87+
usePrefetchInfiniteQuery({
88+
queryKey: ['key'],
89+
initialPageParam: 1,
90+
getNextPageParam: () => 1,
91+
// @ts-expect-error
92+
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
93+
}),
94+
)
95+
})
96+
})
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
2+
import { nextTick, ref } from 'vue-demi'
3+
import { QueryClient } from '../queryClient'
4+
import { usePrefetchInfiniteQuery } from '../usePrefetchInfiniteQuery'
5+
6+
describe('usePrefetchInfiniteQuery', () => {
7+
beforeEach(() => {
8+
vi.useFakeTimers()
9+
})
10+
11+
afterEach(() => {
12+
vi.useRealTimers()
13+
})
14+
15+
test('should prefetch infinite query if query state does not exist', () => {
16+
const queryClient = new QueryClient()
17+
const prefetchInfiniteQuerySpy = vi.spyOn(
18+
queryClient,
19+
'prefetchInfiniteQuery',
20+
)
21+
const queryFn = vi.fn(() =>
22+
Promise.resolve({ data: 'prefetched', currentPage: 1 }),
23+
)
24+
25+
usePrefetchInfiniteQuery(
26+
{
27+
queryKey: ['prefetch-infinite-query'],
28+
queryFn,
29+
initialPageParam: 1,
30+
getNextPageParam: () => undefined,
31+
},
32+
queryClient,
33+
)
34+
35+
expect(prefetchInfiniteQuerySpy).toHaveBeenCalledTimes(1)
36+
expect(prefetchInfiniteQuerySpy).toHaveBeenCalledWith({
37+
queryKey: ['prefetch-infinite-query'],
38+
queryFn,
39+
initialPageParam: 1,
40+
getNextPageParam: expect.any(Function),
41+
})
42+
})
43+
44+
test('should not prefetch infinite query if query state exists', () => {
45+
const queryClient = new QueryClient()
46+
const prefetchInfiniteQuerySpy = vi.spyOn(
47+
queryClient,
48+
'prefetchInfiniteQuery',
49+
)
50+
const queryFn = vi.fn(() =>
51+
Promise.resolve({ data: 'prefetched', currentPage: 1 }),
52+
)
53+
54+
queryClient.setQueryData(['prefetch-infinite-query-existing'], {
55+
pages: [{ data: 'existing', currentPage: 1 }],
56+
pageParams: [1],
57+
})
58+
59+
usePrefetchInfiniteQuery(
60+
{
61+
queryKey: ['prefetch-infinite-query-existing'],
62+
queryFn,
63+
initialPageParam: 1,
64+
getNextPageParam: () => undefined,
65+
},
66+
queryClient,
67+
)
68+
69+
expect(prefetchInfiniteQuerySpy).not.toHaveBeenCalled()
70+
})
71+
72+
test('should unwrap refs in infinite query options', () => {
73+
const queryClient = new QueryClient()
74+
const prefetchInfiniteQuerySpy = vi.spyOn(
75+
queryClient,
76+
'prefetchInfiniteQuery',
77+
)
78+
const nestedRef = ref('value')
79+
80+
usePrefetchInfiniteQuery(
81+
{
82+
queryKey: ['prefetch-infinite-query-ref', nestedRef],
83+
queryFn: () => Promise.resolve({ data: 'prefetched', currentPage: 1 }),
84+
initialPageParam: 1,
85+
getNextPageParam: () => undefined,
86+
},
87+
queryClient,
88+
)
89+
90+
expect(prefetchInfiniteQuerySpy).toHaveBeenCalledWith(
91+
expect.objectContaining({
92+
queryKey: ['prefetch-infinite-query-ref', 'value'],
93+
}),
94+
)
95+
})
96+
97+
test('should prefetch infinite query again when query key changes reactively', async () => {
98+
const queryClient = new QueryClient()
99+
const prefetchInfiniteQuerySpy = vi.spyOn(
100+
queryClient,
101+
'prefetchInfiniteQuery',
102+
)
103+
const keyRef = ref('first')
104+
105+
usePrefetchInfiniteQuery(
106+
() => ({
107+
queryKey: ['prefetch-infinite-query-reactive', keyRef.value],
108+
queryFn: () => Promise.resolve({ data: keyRef.value, currentPage: 1 }),
109+
initialPageParam: 1,
110+
getNextPageParam: () => undefined,
111+
}),
112+
queryClient,
113+
)
114+
115+
expect(prefetchInfiniteQuerySpy).toHaveBeenCalledTimes(1)
116+
expect(prefetchInfiniteQuerySpy).toHaveBeenLastCalledWith(
117+
expect.objectContaining({
118+
queryKey: ['prefetch-infinite-query-reactive', 'first'],
119+
}),
120+
)
121+
122+
keyRef.value = 'second'
123+
await nextTick()
124+
125+
expect(prefetchInfiniteQuerySpy).toHaveBeenCalledTimes(2)
126+
expect(prefetchInfiniteQuerySpy).toHaveBeenLastCalledWith(
127+
expect.objectContaining({
128+
queryKey: ['prefetch-infinite-query-reactive', 'second'],
129+
}),
130+
)
131+
})
132+
133+
test('should warn when used outside of setup function in development mode', () => {
134+
vi.stubEnv('NODE_ENV', 'development')
135+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
136+
137+
try {
138+
usePrefetchInfiniteQuery(
139+
{
140+
queryKey: ['outside-scope-prefetch-infinite-query'],
141+
queryFn: () =>
142+
Promise.resolve({ data: 'prefetched', currentPage: 1 }),
143+
initialPageParam: 1,
144+
getNextPageParam: () => undefined,
145+
},
146+
new QueryClient(),
147+
)
148+
149+
expect(warnSpy).toHaveBeenCalledWith(
150+
'vue-query composable like "useQuery()" should only be used inside a "setup()" function or a running effect scope. They might otherwise lead to memory leaks.',
151+
)
152+
} finally {
153+
warnSpy.mockRestore()
154+
vi.unstubAllEnvs()
155+
}
156+
})
157+
})
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { assertType, describe, expectTypeOf, it } from 'vitest'
2+
import { ref } from 'vue-demi'
3+
import { skipToken } from '@tanstack/query-core'
4+
import { usePrefetchQuery } from '..'
5+
6+
describe('usePrefetchQuery', () => {
7+
it('should return nothing', () => {
8+
const result = usePrefetchQuery({
9+
queryKey: ['key'],
10+
queryFn: () => Promise.resolve(5),
11+
})
12+
13+
expectTypeOf(result).toEqualTypeOf<void>()
14+
})
15+
16+
it('should not allow refetchInterval, enabled or throwOnError options', () => {
17+
assertType(
18+
usePrefetchQuery({
19+
queryKey: ['key'],
20+
queryFn: () => Promise.resolve(5),
21+
// @ts-expect-error TS2353
22+
refetchInterval: 1000,
23+
}),
24+
)
25+
26+
assertType(
27+
usePrefetchQuery({
28+
queryKey: ['key'],
29+
queryFn: () => Promise.resolve(5),
30+
// @ts-expect-error TS2353
31+
enabled: true,
32+
}),
33+
)
34+
35+
assertType(
36+
usePrefetchQuery({
37+
queryKey: ['key'],
38+
queryFn: () => Promise.resolve(5),
39+
// @ts-expect-error TS2353
40+
throwOnError: true,
41+
}),
42+
)
43+
})
44+
45+
it('should accept refs in query options', () => {
46+
assertType(
47+
usePrefetchQuery({
48+
queryKey: ['key', ref('id')],
49+
queryFn: () => Promise.resolve(5),
50+
staleTime: ref(1000),
51+
}),
52+
)
53+
})
54+
55+
it('should not allow skipToken in queryFn', () => {
56+
assertType(
57+
usePrefetchQuery({
58+
queryKey: ['key'],
59+
// @ts-expect-error
60+
queryFn: skipToken,
61+
}),
62+
)
63+
64+
assertType(
65+
usePrefetchQuery({
66+
queryKey: ['key'],
67+
// @ts-expect-error
68+
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
69+
}),
70+
)
71+
})
72+
})

0 commit comments

Comments
 (0)