Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/puny-poems-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/vue-query': minor
---

Add usePrefetchQuery and usePrefetchInfiniteQuery to vue-query.
12 changes: 8 additions & 4 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,14 @@
"label": "infiniteQueryOptions",
"to": "framework/vue/reference/infiniteQueryOptions"
},
{
"label": "usePrefetchQuery",
"to": "framework/vue/reference/usePrefetchQuery"
},
{
"label": "usePrefetchInfiniteQuery",
"to": "framework/vue/reference/usePrefetchInfiniteQuery"
},
{
"label": "hydration",
"to": "framework/vue/reference/hydration"
Expand Down Expand Up @@ -1305,10 +1313,6 @@
{
"label": "Mutation Property Order",
"to": "eslint/mutation-property-order"
},
{
"label": "Prefer Query Options",
"to": "eslint/prefer-query-options"
}
]
},
Expand Down
6 changes: 6 additions & 0 deletions docs/framework/vue/reference/usePrefetchInfiniteQuery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
id: usePrefetchInfiniteQuery
title: usePrefetchInfiniteQuery
ref: docs/framework/react/reference/usePrefetchInfiniteQuery.md
replace: { '@tanstack/react-query': '@tanstack/vue-query' }
---
6 changes: 6 additions & 0 deletions docs/framework/vue/reference/usePrefetchQuery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
id: usePrefetchQuery
title: usePrefetchQuery
ref: docs/framework/react/reference/usePrefetchQuery.md
replace: { '@tanstack/react-query': '@tanstack/vue-query' }
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { assertType, describe, expectTypeOf, it } from 'vitest'
import { ref } from 'vue-demi'
import { skipToken } from '@tanstack/query-core'
import { usePrefetchInfiniteQuery } from '..'

describe('usePrefetchInfiniteQuery', () => {
it('should return nothing', () => {
const result = usePrefetchInfiniteQuery({
queryKey: ['key'],
queryFn: () => Promise.resolve(5),
initialPageParam: 1,
getNextPageParam: () => 1,
})

expectTypeOf(result).toEqualTypeOf<void>()
})

it('should require initialPageParam and getNextPageParam', () => {
assertType(
// @ts-expect-error TS2345
usePrefetchInfiniteQuery({
queryKey: ['key'],
queryFn: () => Promise.resolve(5),
}),
)
})

it('should not allow refetchInterval, enabled or throwOnError options', () => {
assertType(
usePrefetchInfiniteQuery({
queryKey: ['key'],
queryFn: () => Promise.resolve(5),
initialPageParam: 1,
getNextPageParam: () => 1,
// @ts-expect-error TS2353
refetchInterval: 1000,
}),
)

assertType(
usePrefetchInfiniteQuery({
queryKey: ['key'],
queryFn: () => Promise.resolve(5),
initialPageParam: 1,
getNextPageParam: () => 1,
// @ts-expect-error TS2353
enabled: true,
}),
)

assertType(
usePrefetchInfiniteQuery({
queryKey: ['key'],
queryFn: () => Promise.resolve(5),
initialPageParam: 1,
getNextPageParam: () => 1,
// @ts-expect-error TS2353
throwOnError: true,
}),
)
})

it('should accept refs in infinite query options', () => {
assertType(
usePrefetchInfiniteQuery({
queryKey: ['key', ref('id')],
queryFn: () => Promise.resolve(5),
initialPageParam: ref(1),
getNextPageParam: () => 1,
staleTime: ref(1000),
}),
)
})

it('should not allow skipToken in queryFn', () => {
assertType(
usePrefetchInfiniteQuery({
queryKey: ['key'],
initialPageParam: 1,
getNextPageParam: () => 1,
// @ts-expect-error
queryFn: skipToken,
}),
)

assertType(
usePrefetchInfiniteQuery({
queryKey: ['key'],
initialPageParam: 1,
getNextPageParam: () => 1,
// @ts-expect-error
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
}),
)
})
})
157 changes: 157 additions & 0 deletions packages/vue-query/src/__tests__/usePrefetchInfiniteQuery.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
import { nextTick, ref } from 'vue-demi'
import { QueryClient } from '../queryClient'
import { usePrefetchInfiniteQuery } from '../usePrefetchInfiniteQuery'

describe('usePrefetchInfiniteQuery', () => {
beforeEach(() => {
vi.useFakeTimers()
})

afterEach(() => {
vi.useRealTimers()
})

test('should prefetch infinite query if query state does not exist', () => {
const queryClient = new QueryClient()
const prefetchInfiniteQuerySpy = vi.spyOn(
queryClient,
'prefetchInfiniteQuery',
)
const queryFn = vi.fn(() =>
Promise.resolve({ data: 'prefetched', currentPage: 1 }),
)

usePrefetchInfiniteQuery(
{
queryKey: ['prefetch-infinite-query'],
queryFn,
initialPageParam: 1,
getNextPageParam: () => undefined,
},
queryClient,
)

expect(prefetchInfiniteQuerySpy).toHaveBeenCalledTimes(1)
expect(prefetchInfiniteQuerySpy).toHaveBeenCalledWith({
queryKey: ['prefetch-infinite-query'],
queryFn,
initialPageParam: 1,
getNextPageParam: expect.any(Function),
})
})

test('should not prefetch infinite query if query state exists', () => {
const queryClient = new QueryClient()
const prefetchInfiniteQuerySpy = vi.spyOn(
queryClient,
'prefetchInfiniteQuery',
)
const queryFn = vi.fn(() =>
Promise.resolve({ data: 'prefetched', currentPage: 1 }),
)

queryClient.setQueryData(['prefetch-infinite-query-existing'], {
pages: [{ data: 'existing', currentPage: 1 }],
pageParams: [1],
})

usePrefetchInfiniteQuery(
{
queryKey: ['prefetch-infinite-query-existing'],
queryFn,
initialPageParam: 1,
getNextPageParam: () => undefined,
},
queryClient,
)

expect(prefetchInfiniteQuerySpy).not.toHaveBeenCalled()
})

test('should unwrap refs in infinite query options', () => {
const queryClient = new QueryClient()
const prefetchInfiniteQuerySpy = vi.spyOn(
queryClient,
'prefetchInfiniteQuery',
)
const nestedRef = ref('value')

usePrefetchInfiniteQuery(
{
queryKey: ['prefetch-infinite-query-ref', nestedRef],
queryFn: () => Promise.resolve({ data: 'prefetched', currentPage: 1 }),
initialPageParam: 1,
getNextPageParam: () => undefined,
},
queryClient,
)

expect(prefetchInfiniteQuerySpy).toHaveBeenCalledWith(
expect.objectContaining({
queryKey: ['prefetch-infinite-query-ref', 'value'],
}),
)
})

test('should prefetch infinite query again when query key changes reactively', async () => {
const queryClient = new QueryClient()
const prefetchInfiniteQuerySpy = vi.spyOn(
queryClient,
'prefetchInfiniteQuery',
)
const keyRef = ref('first')

usePrefetchInfiniteQuery(
() => ({
queryKey: ['prefetch-infinite-query-reactive', keyRef.value],
queryFn: () => Promise.resolve({ data: keyRef.value, currentPage: 1 }),
initialPageParam: 1,
getNextPageParam: () => undefined,
}),
queryClient,
)

expect(prefetchInfiniteQuerySpy).toHaveBeenCalledTimes(1)
expect(prefetchInfiniteQuerySpy).toHaveBeenLastCalledWith(
expect.objectContaining({
queryKey: ['prefetch-infinite-query-reactive', 'first'],
}),
)

keyRef.value = 'second'
await nextTick()

expect(prefetchInfiniteQuerySpy).toHaveBeenCalledTimes(2)
expect(prefetchInfiniteQuerySpy).toHaveBeenLastCalledWith(
expect.objectContaining({
queryKey: ['prefetch-infinite-query-reactive', 'second'],
}),
)
})

test('should warn when used outside of setup function in development mode', () => {
vi.stubEnv('NODE_ENV', 'development')
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})

try {
usePrefetchInfiniteQuery(
{
queryKey: ['outside-scope-prefetch-infinite-query'],
queryFn: () =>
Promise.resolve({ data: 'prefetched', currentPage: 1 }),
initialPageParam: 1,
getNextPageParam: () => undefined,
},
new QueryClient(),
)

expect(warnSpy).toHaveBeenCalledWith(
'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.',
)
} finally {
warnSpy.mockRestore()
vi.unstubAllEnvs()
}
})
})
72 changes: 72 additions & 0 deletions packages/vue-query/src/__tests__/usePrefetchQuery.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { assertType, describe, expectTypeOf, it } from 'vitest'
import { ref } from 'vue-demi'
import { skipToken } from '@tanstack/query-core'
import { usePrefetchQuery } from '..'

describe('usePrefetchQuery', () => {
it('should return nothing', () => {
const result = usePrefetchQuery({
queryKey: ['key'],
queryFn: () => Promise.resolve(5),
})

expectTypeOf(result).toEqualTypeOf<void>()
})

it('should not allow refetchInterval, enabled or throwOnError options', () => {
assertType(
usePrefetchQuery({
queryKey: ['key'],
queryFn: () => Promise.resolve(5),
// @ts-expect-error TS2353
refetchInterval: 1000,
}),
)

assertType(
usePrefetchQuery({
queryKey: ['key'],
queryFn: () => Promise.resolve(5),
// @ts-expect-error TS2353
enabled: true,
}),
)

assertType(
usePrefetchQuery({
queryKey: ['key'],
queryFn: () => Promise.resolve(5),
// @ts-expect-error TS2353
throwOnError: true,
}),
)
})

it('should accept refs in query options', () => {
assertType(
usePrefetchQuery({
queryKey: ['key', ref('id')],
queryFn: () => Promise.resolve(5),
staleTime: ref(1000),
}),
)
})

it('should not allow skipToken in queryFn', () => {
assertType(
usePrefetchQuery({
queryKey: ['key'],
// @ts-expect-error
queryFn: skipToken,
}),
)

assertType(
usePrefetchQuery({
queryKey: ['key'],
// @ts-expect-error
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
}),
)
})
})
Loading