From c9386be10fca443a46328008f127093c86d3dd13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciar=C3=A1n=20Curley?= Date: Fri, 8 May 2026 18:59:55 +0100 Subject: [PATCH] feat(vue-query): add new methods to vue-query --- .changeset/cold-islands-move.md | 5 + .../__tests__/infiniteQueryOptions.test-d.ts | 37 ++- .../src/__tests__/queryClient.test-d.ts | 106 ++++++++ .../src/__tests__/queryClient.test.ts | 128 +++++++++- .../src/__tests__/queryOptions.test-d.ts | 24 +- .../usePrefetchInfiniteQuery.test-d.ts | 35 +-- .../usePrefetchInfiniteQuery.test.ts | 35 +-- .../src/__tests__/usePrefetchQuery.test-d.ts | 29 +-- .../src/__tests__/usePrefetchQuery.test.ts | 23 +- packages/vue-query/src/queryClient.ts | 234 +++++++++++++++++- packages/vue-query/src/types.ts | 63 +++++ .../vue-query/src/usePrefetchInfiniteQuery.ts | 117 +++------ packages/vue-query/src/usePrefetchQuery.ts | 60 ++--- packages/vue-query/src/utils.ts | 6 +- 14 files changed, 686 insertions(+), 216 deletions(-) create mode 100644 .changeset/cold-islands-move.md diff --git a/.changeset/cold-islands-move.md b/.changeset/cold-islands-move.md new file mode 100644 index 00000000000..bd70eb2c665 --- /dev/null +++ b/.changeset/cold-islands-move.md @@ -0,0 +1,5 @@ +--- +'@tanstack/vue-query': minor +--- + +add new imperitive methods to QueryClient proxy diff --git a/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts b/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts index 2b15cb7bfed..5e09f5c0b44 100644 --- a/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts +++ b/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts @@ -1,6 +1,6 @@ import { assertType, describe, expectTypeOf, it } from 'vitest' import { dataTagSymbol } from '@tanstack/query-core' -import { reactive } from 'vue-demi' +import { reactive, unref } from 'vue-demi' import { queryKey } from '@tanstack/query-test-utils' import { infiniteQueryOptions } from '../infiniteQueryOptions' import { QueryClient } from '../queryClient' @@ -49,6 +49,41 @@ describe('infiniteQueryOptions', () => { InfiniteData | undefined >() }) + it('should work when passed to infiniteQuery', async () => { + const options = infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + getNextPageParam: () => 1, + initialPageParam: 1, + }) + + const data = await new QueryClient().infiniteQuery({ + ...unref(options), + enabled: true, + staleTime: 0, + pages: 1, + }) + + expectTypeOf(data).toEqualTypeOf>() + }) + it('should work when passed to infiniteQuery with select', async () => { + const options = infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + getNextPageParam: () => 1, + initialPageParam: 1, + select: (data) => data.pages, + }) + + const data = await new QueryClient().infiniteQuery({ + ...unref(options), + enabled: true, + staleTime: 0, + pages: 1, + }) + + expectTypeOf(data).toEqualTypeOf>() + }) it('should tag the queryKey with the result type of the QueryFn', () => { const key = queryKey() const { queryKey: tagged } = infiniteQueryOptions({ diff --git a/packages/vue-query/src/__tests__/queryClient.test-d.ts b/packages/vue-query/src/__tests__/queryClient.test-d.ts index cd3af0d5618..cab8f59c007 100644 --- a/packages/vue-query/src/__tests__/queryClient.test-d.ts +++ b/packages/vue-query/src/__tests__/queryClient.test-d.ts @@ -1,4 +1,5 @@ import { assertType, describe, expectTypeOf, it } from 'vitest' +import { skipToken } from '@tanstack/query-core' import { queryKey } from '@tanstack/query-test-utils' import { QueryClient } from '../queryClient' import type { DataTag, InfiniteData } from '@tanstack/query-core' @@ -151,3 +152,108 @@ describe('fetchInfiniteQuery', () => { ]) }) }) + +describe('query', () => { + it('should return the selected type', () => { + const result = new QueryClient().query({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + select: (data) => data.length, + }) + + expectTypeOf(result).toEqualTypeOf>() + }) + + it('should infer select type with skipToken', () => { + const result = new QueryClient().query({ + queryKey: ['key'], + queryFn: skipToken, + select: (data: string) => data.length, + }) + + expectTypeOf(result).toEqualTypeOf>() + }) + + it('should infer select type with skipToken and enabled false', () => { + const result = new QueryClient().query({ + queryKey: ['key'], + queryFn: skipToken, + enabled: false, + select: (data: string) => data.length, + }) + + expectTypeOf(result).toEqualTypeOf>() + }) + + it('should infer select type with skipToken and enabled true', () => { + const result = new QueryClient().query({ + queryKey: ['key'], + queryFn: skipToken, + enabled: true, + select: (data: string) => data.length, + }) + + expectTypeOf(result).toEqualTypeOf>() + }) +}) + +describe('infiniteQuery', () => { + it('should return infinite data', async () => { + const data = await new QueryClient().infiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + getNextPageParam: () => 1, + initialPageParam: 1, + }) + + expectTypeOf(data).toEqualTypeOf>() + }) + + it('should return the selected type', () => { + const result = new QueryClient().infiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve({ count: 1 }), + getNextPageParam: () => 2, + initialPageParam: 1, + select: (data) => data.pages.map((page) => page.count), + }) + + expectTypeOf(result).toEqualTypeOf>>() + }) + + it('should allow passing pages with getNextPageParam', () => { + assertType>([ + { + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + initialPageParam: 1, + getNextPageParam: () => 1, + pages: 5, + }, + ]) + }) + + it('should not allow passing pages without getNextPageParam', () => { + assertType>([ + // @ts-expect-error Property 'getNextPageParam' is missing + { + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + initialPageParam: 1, + pages: 5, + }, + ]) + }) + + it('should preserve page param inference', () => { + new QueryClient().infiniteQuery({ + queryKey: ['key'], + queryFn: ({ pageParam }) => { + expectTypeOf(pageParam).toEqualTypeOf() + return Promise.resolve(pageParam.toString()) + }, + initialPageParam: 1, + getNextPageParam: () => undefined, + }) + }) +}) diff --git a/packages/vue-query/src/__tests__/queryClient.test.ts b/packages/vue-query/src/__tests__/queryClient.test.ts index 61f17739f9d..fb82c3c5144 100644 --- a/packages/vue-query/src/__tests__/queryClient.test.ts +++ b/packages/vue-query/src/__tests__/queryClient.test.ts @@ -1,8 +1,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { ref } from 'vue-demi' +import { ref, unref } from 'vue-demi' import { QueryClient as QueryClientOrigin } from '@tanstack/query-core' import { QueryClient } from '../queryClient' import { infiniteQueryOptions } from '../infiniteQueryOptions' +import { queryOptions } from '../queryOptions' vi.mock('@tanstack/query-core', async () => { const actual = await vi.importActual<{ @@ -340,6 +341,57 @@ describe('QueryCache', () => { }) }) + describe('query', () => { + it('should properly unwrap queryKey', () => { + const queryClient = new QueryClient() + + queryClient.query({ + queryKey: queryKeyRef, + }) + + expect(QueryClientOrigin.prototype.query).toBeCalledWith({ + queryKey: queryKeyUnref, + }) + }) + + it('should properly unwrap enabled, staleTime, and select', () => { + const queryClient = new QueryClient() + const enabled = () => false + const staleTime = () => 1000 + const select = (data: string) => data.length + + queryClient.query({ + queryKey: queryKeyRef, + enabled: ref(enabled), + staleTime: ref(staleTime), + select: ref(select), + }) + + expect(QueryClientOrigin.prototype.query).toBeCalledWith({ + queryKey: queryKeyUnref, + enabled, + staleTime, + select, + }) + }) + + it('should properly unwrap queryOptions getter', () => { + const queryClient = new QueryClient() + + const options = queryOptions(() => ({ + queryKey: queryKeyRef, + queryFn: fn, + })) + + queryClient.query(options) + + expect(QueryClientOrigin.prototype.query).toBeCalledWith({ + queryKey: queryKeyUnref, + queryFn: fn, + }) + }) + }) + describe('prefetchQuery', () => { it('should properly unwrap parameters', () => { const queryClient = new QueryClient() @@ -393,6 +445,80 @@ describe('QueryCache', () => { }) }) + describe('infiniteQuery', () => { + it('should properly unwrap queryKey, initialPageParam, pages, and select', () => { + const queryClient = new QueryClient() + const getNextPageParam = () => 1 + const select = (data: { pages: Array }) => data.pages.length + + queryClient.infiniteQuery({ + queryKey: queryKeyRef, + initialPageParam: ref(0), + pages: ref(2), + getNextPageParam: ref(getNextPageParam), + select: ref(select), + }) + + expect(QueryClientOrigin.prototype.infiniteQuery).toBeCalledWith( + expect.objectContaining({ + queryKey: queryKeyUnref, + initialPageParam: 0, + pages: 2, + getNextPageParam, + select, + }), + ) + }) + + it('should properly unwrap getNextPageParam when using infiniteQueryOptions', () => { + const queryClient = new QueryClient() + const getNextPageParam = () => 12 + + const options = infiniteQueryOptions({ + queryKey: queryKeyRef, + initialPageParam: ref(0), + getNextPageParam: ref(getNextPageParam), + }) + + queryClient.infiniteQuery({ + ...unref(options), + enabled: true, + staleTime: 0, + pages: 1, + }) + + expect(QueryClientOrigin.prototype.infiniteQuery).toBeCalledWith( + expect.objectContaining({ + queryKey: queryKeyUnref, + initialPageParam: 0, + pages: 1, + getNextPageParam, + }), + ) + }) + + it('should properly unwrap options getter', () => { + const queryClient = new QueryClient() + const getNextPageParam = () => 12 + + queryClient.infiniteQuery(() => ({ + queryKey: queryKeyRef, + initialPageParam: ref(0), + pages: ref(1), + getNextPageParam: ref(getNextPageParam), + })) + + expect(QueryClientOrigin.prototype.infiniteQuery).toBeCalledWith( + expect.objectContaining({ + queryKey: queryKeyUnref, + initialPageParam: 0, + pages: 1, + getNextPageParam, + }), + ) + }) + }) + describe('prefetchInfiniteQuery', () => { it('should properly unwrap parameters', () => { const queryClient = new QueryClient() diff --git a/packages/vue-query/src/__tests__/queryOptions.test-d.ts b/packages/vue-query/src/__tests__/queryOptions.test-d.ts index a3e8c6d0d41..861b8f2e750 100644 --- a/packages/vue-query/src/__tests__/queryOptions.test-d.ts +++ b/packages/vue-query/src/__tests__/queryOptions.test-d.ts @@ -39,6 +39,25 @@ describe('queryOptions', () => { const { data } = reactive(useQuery(options)) expectTypeOf(data).toEqualTypeOf() }) + it('should work when passed to query', async () => { + const options = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + }) + + const data = await new QueryClient().query(options) + expectTypeOf(data).toEqualTypeOf() + }) + it('should work when passed to query with select', async () => { + const options = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + select: (data) => data.toString(), + }) + + const data = await new QueryClient().query(options) + expectTypeOf(data).toEqualTypeOf() + }) it('should tag the queryKey with the result type of the QueryFn', () => { const key = queryKey() const { queryKey: tagged } = queryOptions({ @@ -132,10 +151,13 @@ describe('queryOptions', () => { // Should not error const data = queryClient.invalidateQueries(options) // Should not error - const data2 = queryClient.fetchQuery(options) + const data2 = queryClient.query(options) + // Should not error + const data3 = queryClient.fetchQuery(options) expectTypeOf(data).toEqualTypeOf>() expectTypeOf(data2).toEqualTypeOf>() + expectTypeOf(data3).toEqualTypeOf>() }) it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => { diff --git a/packages/vue-query/src/__tests__/usePrefetchInfiniteQuery.test-d.ts b/packages/vue-query/src/__tests__/usePrefetchInfiniteQuery.test-d.ts index 45fe98e1511..cefb4fe61d0 100644 --- a/packages/vue-query/src/__tests__/usePrefetchInfiniteQuery.test-d.ts +++ b/packages/vue-query/src/__tests__/usePrefetchInfiniteQuery.test-d.ts @@ -1,6 +1,5 @@ import { assertType, describe, expectTypeOf, it } from 'vitest' import { ref } from 'vue-demi' -import { skipToken } from '@tanstack/query-core' import { queryKey } from '@tanstack/query-test-utils' import { usePrefetchInfiniteQuery } from '..' @@ -26,7 +25,7 @@ describe('usePrefetchInfiniteQuery', () => { ) }) - it('should not allow refetchInterval, enabled or throwOnError options', () => { + it('should not allow refetchInterval or throwOnError options', () => { assertType( usePrefetchInfiniteQuery({ queryKey: queryKey(), @@ -38,17 +37,6 @@ describe('usePrefetchInfiniteQuery', () => { }), ) - assertType( - usePrefetchInfiniteQuery({ - queryKey: queryKey(), - queryFn: () => Promise.resolve(5), - initialPageParam: 1, - getNextPageParam: () => 1, - // @ts-expect-error TS2353 - enabled: true, - }), - ) - assertType( usePrefetchInfiniteQuery({ queryKey: queryKey(), @@ -73,25 +61,4 @@ describe('usePrefetchInfiniteQuery', () => { ) }) - it('should not allow skipToken in queryFn', () => { - assertType( - usePrefetchInfiniteQuery({ - queryKey: queryKey(), - initialPageParam: 1, - getNextPageParam: () => 1, - // @ts-expect-error - queryFn: skipToken, - }), - ) - - assertType( - usePrefetchInfiniteQuery({ - queryKey: queryKey(), - initialPageParam: 1, - getNextPageParam: () => 1, - // @ts-expect-error - queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5), - }), - ) - }) }) diff --git a/packages/vue-query/src/__tests__/usePrefetchInfiniteQuery.test.ts b/packages/vue-query/src/__tests__/usePrefetchInfiniteQuery.test.ts index 2456fa9e62e..08777d53bd3 100644 --- a/packages/vue-query/src/__tests__/usePrefetchInfiniteQuery.test.ts +++ b/packages/vue-query/src/__tests__/usePrefetchInfiniteQuery.test.ts @@ -15,10 +15,7 @@ describe('usePrefetchInfiniteQuery', () => { it('should prefetch infinite query if query state does not exist', () => { const queryClient = new QueryClient() - const prefetchInfiniteQuerySpy = vi.spyOn( - queryClient, - 'prefetchInfiniteQuery', - ) + const infiniteQuerySpy = vi.spyOn(queryClient, 'infiniteQuery') const queryFn = () => Promise.resolve({ data: 'prefetched', currentPage: 1 }) const getNextPageParam = () => undefined @@ -35,8 +32,8 @@ describe('usePrefetchInfiniteQuery', () => { queryClient, ) - expect(prefetchInfiniteQuerySpy).toHaveBeenCalledTimes(1) - expect(prefetchInfiniteQuerySpy).toHaveBeenCalledWith({ + expect(infiniteQuerySpy).toHaveBeenCalledTimes(1) + expect(infiniteQuerySpy).toHaveBeenCalledWith({ queryKey: key, queryFn, initialPageParam: 1, @@ -46,10 +43,7 @@ describe('usePrefetchInfiniteQuery', () => { it('should not prefetch infinite query if query state exists', () => { const queryClient = new QueryClient() - const prefetchInfiniteQuerySpy = vi.spyOn( - queryClient, - 'prefetchInfiniteQuery', - ) + const infiniteQuerySpy = vi.spyOn(queryClient, 'infiniteQuery') const queryFn = () => Promise.resolve({ data: 'prefetched', currentPage: 1 }) @@ -69,15 +63,12 @@ describe('usePrefetchInfiniteQuery', () => { queryClient, ) - expect(prefetchInfiniteQuerySpy).not.toHaveBeenCalled() + expect(infiniteQuerySpy).not.toHaveBeenCalled() }) it('should unwrap refs in infinite query options', () => { const queryClient = new QueryClient() - const prefetchInfiniteQuerySpy = vi.spyOn( - queryClient, - 'prefetchInfiniteQuery', - ) + const infiniteQuerySpy = vi.spyOn(queryClient, 'infiniteQuery') const nestedRef = ref('value') const key = queryKey() const queryFn = () => @@ -94,7 +85,7 @@ describe('usePrefetchInfiniteQuery', () => { queryClient, ) - expect(prefetchInfiniteQuerySpy).toHaveBeenCalledWith({ + expect(infiniteQuerySpy).toHaveBeenCalledWith({ queryKey: [...key, 'value'], queryFn, initialPageParam: 1, @@ -104,10 +95,7 @@ describe('usePrefetchInfiniteQuery', () => { it('should prefetch infinite query again when query key changes reactively', async () => { const queryClient = new QueryClient() - const prefetchInfiniteQuerySpy = vi.spyOn( - queryClient, - 'prefetchInfiniteQuery', - ) + const infiniteQuerySpy = vi.spyOn(queryClient, 'infiniteQuery') const keyRef = ref('first') const key = queryKey() const queryFn = () => @@ -124,7 +112,8 @@ describe('usePrefetchInfiniteQuery', () => { queryClient, ) - expect(prefetchInfiniteQuerySpy).toHaveBeenNthCalledWith(1, { + expect(infiniteQuerySpy).toHaveBeenCalledTimes(1) + expect(infiniteQuerySpy).toHaveBeenNthCalledWith(1, { queryKey: [...key, 'first'], queryFn, initialPageParam: 1, @@ -134,8 +123,8 @@ describe('usePrefetchInfiniteQuery', () => { keyRef.value = 'second' await nextTick() - expect(prefetchInfiniteQuerySpy).toHaveBeenCalledTimes(2) - expect(prefetchInfiniteQuerySpy).toHaveBeenNthCalledWith(2, { + expect(infiniteQuerySpy).toHaveBeenCalledTimes(2) + expect(infiniteQuerySpy).toHaveBeenNthCalledWith(2, { queryKey: [...key, 'second'], queryFn, initialPageParam: 1, diff --git a/packages/vue-query/src/__tests__/usePrefetchQuery.test-d.ts b/packages/vue-query/src/__tests__/usePrefetchQuery.test-d.ts index 85067bc94bb..fe4b766b12f 100644 --- a/packages/vue-query/src/__tests__/usePrefetchQuery.test-d.ts +++ b/packages/vue-query/src/__tests__/usePrefetchQuery.test-d.ts @@ -1,6 +1,5 @@ import { assertType, describe, expectTypeOf, it } from 'vitest' import { ref } from 'vue-demi' -import { skipToken } from '@tanstack/query-core' import { queryKey } from '@tanstack/query-test-utils' import { usePrefetchQuery } from '..' @@ -14,7 +13,7 @@ describe('usePrefetchQuery', () => { expectTypeOf(result).toEqualTypeOf() }) - it('should not allow refetchInterval, enabled or throwOnError options', () => { + it('should not allow refetchInterval or throwOnError options', () => { assertType( usePrefetchQuery({ queryKey: queryKey(), @@ -24,15 +23,6 @@ describe('usePrefetchQuery', () => { }), ) - assertType( - usePrefetchQuery({ - queryKey: queryKey(), - queryFn: () => Promise.resolve(5), - // @ts-expect-error TS2353 - enabled: true, - }), - ) - assertType( usePrefetchQuery({ queryKey: queryKey(), @@ -53,21 +43,4 @@ describe('usePrefetchQuery', () => { ) }) - it('should not allow skipToken in queryFn', () => { - assertType( - usePrefetchQuery({ - queryKey: queryKey(), - // @ts-expect-error - queryFn: skipToken, - }), - ) - - assertType( - usePrefetchQuery({ - queryKey: queryKey(), - // @ts-expect-error - queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5), - }), - ) - }) }) diff --git a/packages/vue-query/src/__tests__/usePrefetchQuery.test.ts b/packages/vue-query/src/__tests__/usePrefetchQuery.test.ts index 4cfcf832c4f..87d7861cdfd 100644 --- a/packages/vue-query/src/__tests__/usePrefetchQuery.test.ts +++ b/packages/vue-query/src/__tests__/usePrefetchQuery.test.ts @@ -15,7 +15,7 @@ describe('usePrefetchQuery', () => { it('should prefetch query if query state does not exist', () => { const queryClient = new QueryClient() - const prefetchQuerySpy = vi.spyOn(queryClient, 'prefetchQuery') + const querySpy = vi.spyOn(queryClient, 'query') const queryFn = () => Promise.resolve('prefetched') const key = queryKey() @@ -27,8 +27,8 @@ describe('usePrefetchQuery', () => { queryClient, ) - expect(prefetchQuerySpy).toHaveBeenCalledTimes(1) - expect(prefetchQuerySpy).toHaveBeenCalledWith({ + expect(querySpy).toHaveBeenCalledTimes(1) + expect(querySpy).toHaveBeenCalledWith({ queryKey: key, queryFn, }) @@ -36,7 +36,7 @@ describe('usePrefetchQuery', () => { it('should not prefetch query if query state exists', () => { const queryClient = new QueryClient() - const prefetchQuerySpy = vi.spyOn(queryClient, 'prefetchQuery') + const querySpy = vi.spyOn(queryClient, 'query') const queryFn = () => Promise.resolve('prefetched') const key = queryKey() queryClient.setQueryData(key, 'existing') @@ -49,12 +49,12 @@ describe('usePrefetchQuery', () => { queryClient, ) - expect(prefetchQuerySpy).not.toHaveBeenCalled() + expect(querySpy).not.toHaveBeenCalled() }) it('should unwrap refs in query options', () => { const queryClient = new QueryClient() - const prefetchQuerySpy = vi.spyOn(queryClient, 'prefetchQuery') + const querySpy = vi.spyOn(queryClient, 'query') const nestedRef = ref('value') const key = queryKey() const queryFn = () => Promise.resolve('prefetched') @@ -67,7 +67,7 @@ describe('usePrefetchQuery', () => { queryClient, ) - expect(prefetchQuerySpy).toHaveBeenCalledWith({ + expect(querySpy).toHaveBeenCalledWith({ queryKey: [...key, 'value'], queryFn, }) @@ -75,7 +75,7 @@ describe('usePrefetchQuery', () => { it('should prefetch again when query key changes reactively', async () => { const queryClient = new QueryClient() - const prefetchQuerySpy = vi.spyOn(queryClient, 'prefetchQuery') + const querySpy = vi.spyOn(queryClient, 'query') const keyRef = ref('first') const key = queryKey() const queryFn = () => Promise.resolve(keyRef.value) @@ -88,7 +88,8 @@ describe('usePrefetchQuery', () => { queryClient, ) - expect(prefetchQuerySpy).toHaveBeenNthCalledWith(1, { + expect(querySpy).toHaveBeenCalledTimes(1) + expect(querySpy).toHaveBeenNthCalledWith(1, { queryKey: [...key, 'first'], queryFn, }) @@ -96,8 +97,8 @@ describe('usePrefetchQuery', () => { keyRef.value = 'second' await nextTick() - expect(prefetchQuerySpy).toHaveBeenCalledTimes(2) - expect(prefetchQuerySpy).toHaveBeenNthCalledWith(2, { + expect(querySpy).toHaveBeenCalledTimes(2) + expect(querySpy).toHaveBeenNthCalledWith(2, { queryKey: [...key, 'second'], queryFn, }) diff --git a/packages/vue-query/src/queryClient.ts b/packages/vue-query/src/queryClient.ts index b9f78e26c17..be040d7684e 100644 --- a/packages/vue-query/src/queryClient.ts +++ b/packages/vue-query/src/queryClient.ts @@ -1,13 +1,21 @@ import { nextTick, ref } from 'vue-demi' import { QueryClient as QC } from '@tanstack/query-core' -import { cloneDeepUnref } from './utils' +import { cloneDeepUnref, toValueDeep } from './utils' import { QueryCache } from './queryCache' import { MutationCache } from './mutationCache' import type { UseQueryOptions } from './useQuery' import type { Ref } from 'vue-demi' -import type { MaybeRefDeep, NoUnknown, QueryClientConfig } from './types' +import type { + DeepUnwrapRef, + InfiniteQueryExecuteOptions, + MaybeRefDeep, + NoUnknown, + QueryClientConfig, + QueryExecuteOptions, +} from './types' import type { CancelOptions, + QueryExecuteOptions as CoreQueryExecuteOptions, DefaultError, DefaultOptions, EnsureQueryDataOptions, @@ -65,6 +73,9 @@ export class QueryClient extends QC { return super.getQueryData(cloneDeepUnref(queryKey)) } + /** + * @deprecated Use queryClient.query({ ...options, staleTime: 'static' }) instead. This method will be removed in the next major version. + */ ensureQueryData< TQueryFnData, TError = DefaultError, @@ -73,6 +84,9 @@ export class QueryClient extends QC { >( options: EnsureQueryDataOptions, ): Promise + /** + * @deprecated Use queryClient.query({ ...options, staleTime: 'static' }) instead. This method will be removed in the next major version. + */ ensureQueryData< TQueryFnData, TError = DefaultError, @@ -260,6 +274,201 @@ export class QueryClient extends QC { ) } + query< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = never, + >( + options: QueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam + >, + ): Promise + query< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = never, + >( + options: + | MaybeRefDeep< + QueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam + > + > + | (() => MaybeRefDeep< + QueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam + > + >), + ): Promise + query< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = never, + >( + options: + | MaybeRefDeep< + QueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam + > + > + | (() => MaybeRefDeep< + QueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam + > + >), + ): Promise { + return super.query( + toValueDeep< + QueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam + > + >(options) as CoreQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryData, + DeepUnwrapRef, + TPageParam + >, + ) + } + + infiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + >( + options: InfiniteQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, + ): Promise< + Array extends Array> + ? InfiniteData + : TData + > + infiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + >( + options: + | MaybeRefDeep< + InfiniteQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + > + | (() => MaybeRefDeep< + InfiniteQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + >), + ): Promise< + Array extends Array> + ? InfiniteData + : TData + > + infiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + >( + options: + | MaybeRefDeep< + InfiniteQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + > + | (() => MaybeRefDeep< + InfiniteQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + >), + ): Promise< + Array extends Array> + ? InfiniteData + : TData + > { + return super.infiniteQuery( + toValueDeep< + InfiniteQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + >(options), + ) + } + + /** + * @deprecated Use queryClient.query(options) instead. This method will be removed in the next major version. + */ fetchQuery< TQueryFnData, TError = DefaultError, @@ -275,6 +484,9 @@ export class QueryClient extends QC { TPageParam >, ): Promise + /** + * @deprecated Use queryClient.query(options) instead. This method will be removed in the next major version. + */ fetchQuery< TQueryFnData, TError = DefaultError, @@ -308,6 +520,9 @@ export class QueryClient extends QC { return super.fetchQuery(cloneDeepUnref(options)) } + /** + * @deprecated Use queryClient.query(options) instead. You can swallow errors with `.catch(noop)`. This method will be removed in the next major version. + */ prefetchQuery< TQueryFnData = unknown, TError = DefaultError, @@ -316,6 +531,9 @@ export class QueryClient extends QC { >( options: FetchQueryOptions, ): Promise + /** + * @deprecated Use queryClient.query(options) instead. You can swallow errors with `.catch(noop)`. This method will be removed in the next major version. + */ prefetchQuery< TQueryFnData = unknown, TError = DefaultError, @@ -339,6 +557,9 @@ export class QueryClient extends QC { return super.prefetchQuery(cloneDeepUnref(options)) } + /** + * @deprecated Use queryClient.infiniteQuery(options) instead. This method will be removed in the next major version. + */ fetchInfiniteQuery< TQueryFnData = unknown, TError = DefaultError, @@ -354,6 +575,9 @@ export class QueryClient extends QC { TPageParam >, ): Promise> + /** + * @deprecated Use queryClient.infiniteQuery(options) instead. This method will be removed in the next major version. + */ fetchInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -391,6 +615,9 @@ export class QueryClient extends QC { return super.fetchInfiniteQuery(cloneDeepUnref(options)) } + /** + * @deprecated use void queryClient.infiniteQuery(options)instead. You can swallow errors with `.catch(noop)`. This method will be removed in the next major version. + */ prefetchInfiniteQuery< TQueryFnData, TError = DefaultError, @@ -406,6 +633,9 @@ export class QueryClient extends QC { TPageParam >, ): Promise + /** + * @deprecated use void queryClient.infiniteQuery(options)instead. You can swallow errors with `.catch(noop)`. This method will be removed in the next major version. + */ prefetchInfiniteQuery< TQueryFnData, TError = DefaultError, diff --git a/packages/vue-query/src/types.ts b/packages/vue-query/src/types.ts index 8ef5664b29f..497b9b7398f 100644 --- a/packages/vue-query/src/types.ts +++ b/packages/vue-query/src/types.ts @@ -2,10 +2,17 @@ import type { DefaultError, DehydrateOptions, HydrateOptions, + // spellchecker is overzealous here + // eslint-disable-next-line cspell/spellchecker + InfiniteQueryExecuteOptions as IQCEOptions, + InfiniteData, MutationCache, MutationObserverOptions, OmitKeyof, + QueryExecuteOptions as QCEOptions, + QueryBooleanOption, QueryCache, + QueryKey, QueryObserverOptions, } from '@tanstack/query-core' import type { ComputedRef, Ref, UnwrapRef } from 'vue-demi' @@ -64,6 +71,62 @@ export type ShallowOption = { shallow?: boolean } +export type QueryExecuteOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = { + [Property in keyof QCEOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam + >]: Property extends 'enabled' + ? + | MaybeRefOrGetter + | (() => QueryBooleanOption< + TQueryFnData, + TError, + TQueryData, + DeepUnwrapRef + >) + | QCEOptions< + TQueryFnData, + TError, + TData, + TQueryData, + DeepUnwrapRef, + TPageParam + >[Property] + : QCEOptions< + TQueryFnData, + TError, + TData, + TQueryData, + DeepUnwrapRef, + TPageParam + >[Property] +} + +export type InfiniteQueryExecuteOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = IQCEOptions< + TQueryFnData, + TError, + TData, + DeepUnwrapRef, + TPageParam +> + export type MutationOptions< TData = unknown, TError = DefaultError, diff --git a/packages/vue-query/src/usePrefetchInfiniteQuery.ts b/packages/vue-query/src/usePrefetchInfiniteQuery.ts index ff6ff23c0aa..7ec28e26ca5 100644 --- a/packages/vue-query/src/usePrefetchInfiniteQuery.ts +++ b/packages/vue-query/src/usePrefetchInfiniteQuery.ts @@ -1,29 +1,10 @@ -import { getCurrentScope, unref, watchEffect } from 'vue-demi' +import { getCurrentScope, watchEffect } from 'vue-demi' +import { noop } from '@tanstack/query-core' import { useQueryClient } from './useQueryClient' -import { cloneDeepUnref } from './utils' -import type { - DefaultError, - FetchInfiniteQueryOptions, - FetchQueryOptions, - GetNextPageParamFunction, - InfiniteData, - InitialPageParam, - OmitKeyof, - QueryKey, - SkipToken, -} from '@tanstack/query-core' +import { toValueDeep } from './utils' +import type { DefaultError, QueryKey } from '@tanstack/query-core' import type { QueryClient } from './queryClient' -import type { MaybeRefDeep, MaybeRefOrGetter } from './types' - -type PrefetchInfinitePages = - | { - pages?: never - getNextPageParam?: GetNextPageParamFunction - } - | { - pages: number - getNextPageParam: GetNextPageParamFunction - } +import type { InfiniteQueryExecuteOptions, MaybeRefDeep } from './types' export type UsePrefetchInfiniteQueryOptions< TQueryFnData, @@ -31,32 +12,13 @@ export type UsePrefetchInfiniteQueryOptions< TData, TQueryKey extends QueryKey, TPageParam, -> = OmitKeyof< - FetchQueryOptions< - TQueryFnData, - TError, - InfiniteData, - TQueryKey, - TPageParam - >, - 'queryFn' | 'initialPageParam' -> & - InitialPageParam & { - queryFn?: Exclude< - FetchQueryOptions< - TQueryFnData, - TError, - InfiniteData, - TQueryKey, - TPageParam - >['queryFn'], - SkipToken - > - } & PrefetchInfinitePages - -function isGetter(value: MaybeRefOrGetter): value is () => T { - return typeof value === 'function' -} +> = InfiniteQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> export function usePrefetchInfiniteQuery< TQueryFnData = unknown, @@ -65,17 +27,25 @@ export function usePrefetchInfiniteQuery< TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: MaybeRefOrGetter< - MaybeRefDeep< - UsePrefetchInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam + options: + | MaybeRefDeep< + UsePrefetchInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > > - > - >, + | (() => MaybeRefDeep< + UsePrefetchInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + >), queryClient?: QueryClient, ): void { if (process.env.NODE_ENV === 'development') { @@ -89,25 +59,18 @@ export function usePrefetchInfiniteQuery< const client = queryClient || useQueryClient() watchEffect(() => { - const resolvedOptions = isGetter(options) ? options() : unref(options) - const clonedOptions: UsePrefetchInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - > = cloneDeepUnref(resolvedOptions) + const clonedOptions = toValueDeep< + UsePrefetchInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + >(options) if (!client.getQueryState(clonedOptions.queryKey)) { - void client.prefetchInfiniteQuery( - clonedOptions as FetchInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - >, - ) + void client.infiniteQuery(clonedOptions).then(noop).catch(noop) } }) } diff --git a/packages/vue-query/src/usePrefetchQuery.ts b/packages/vue-query/src/usePrefetchQuery.ts index 85f239c54b4..32689c86431 100644 --- a/packages/vue-query/src/usePrefetchQuery.ts +++ b/packages/vue-query/src/usePrefetchQuery.ts @@ -1,34 +1,24 @@ -import { getCurrentScope, unref, watchEffect } from 'vue-demi' +import { getCurrentScope, watchEffect } from 'vue-demi' +import { noop } from '@tanstack/query-core' import { useQueryClient } from './useQueryClient' -import { cloneDeepUnref } from './utils' -import type { - DefaultError, - FetchQueryOptions, - OmitKeyof, - QueryKey, - SkipToken, -} from '@tanstack/query-core' +import { toValueDeep } from './utils' +import type { DefaultError, QueryKey } from '@tanstack/query-core' import type { QueryClient } from './queryClient' -import type { MaybeRefDeep, MaybeRefOrGetter } from './types' +import type { MaybeRefDeep, QueryExecuteOptions } from './types' export type UsePrefetchQueryOptions< TQueryFnData, TError, TData, TQueryKey extends QueryKey, -> = OmitKeyof< - FetchQueryOptions, - 'queryFn' -> & { - queryFn?: Exclude< - FetchQueryOptions['queryFn'], - SkipToken - > -} - -function isGetter(value: MaybeRefOrGetter): value is () => T { - return typeof value === 'function' -} +> = QueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + never +> export function usePrefetchQuery< TQueryFnData = unknown, @@ -36,11 +26,13 @@ export function usePrefetchQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: MaybeRefOrGetter< - MaybeRefDeep< - UsePrefetchQueryOptions - > - >, + options: + | MaybeRefDeep< + UsePrefetchQueryOptions + > + | (() => MaybeRefDeep< + UsePrefetchQueryOptions + >), queryClient?: QueryClient, ): void { if (process.env.NODE_ENV === 'development') { @@ -54,16 +46,12 @@ export function usePrefetchQuery< const client = queryClient || useQueryClient() watchEffect(() => { - const resolvedOptions = isGetter(options) ? options() : unref(options) - const clonedOptions: UsePrefetchQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey - > = cloneDeepUnref(resolvedOptions) + const clonedOptions = toValueDeep< + UsePrefetchQueryOptions + >(options) if (!client.getQueryState(clonedOptions.queryKey)) { - void client.prefetchQuery(clonedOptions) + void client.query(clonedOptions).then(noop).catch(noop) } }) } diff --git a/packages/vue-query/src/utils.ts b/packages/vue-query/src/utils.ts index 8d644f224ea..913d5af8399 100644 --- a/packages/vue-query/src/utils.ts +++ b/packages/vue-query/src/utils.ts @@ -110,6 +110,8 @@ function isFunction(value: unknown): value is Function { return typeof value === 'function' } -export function toValueDeep(source: (() => T) | MaybeRefDeep): T { - return isFunction(source) ? source() : cloneDeepUnref(source) +export function toValueDeep( + source: (() => MaybeRefDeep) | MaybeRefDeep, +): T { + return cloneDeepUnref(isFunction(source) ? source() : source) }