diff --git a/.changeset/cold-islands-move.md b/.changeset/cold-islands-move.md new file mode 100644 index 0000000000..bd70eb2c66 --- /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/docs/framework/vue/guides/prefetching.md b/docs/framework/vue/guides/prefetching.md index 3946434878..12617657c3 100644 --- a/docs/framework/vue/guides/prefetching.md +++ b/docs/framework/vue/guides/prefetching.md @@ -3,24 +3,30 @@ id: prefetching title: Prefetching --- -If you're lucky enough, you may know enough about what your users will do to be able to prefetch the data they need before it's needed! If this is the case, you can use the `prefetchQuery` method to prefetch the results of a query to be placed into the cache: +If you're lucky enough, you may know enough about what your users will do to be able to prefetch the data they need before it's needed. If this is the case, use `queryClient.query` or `queryClient.infiniteQuery` to warm the cache ahead of time: [//]: # 'ExamplePrefetching' ```tsx +import { noop } from '@tanstack/vue-query' + const prefetchTodos = async () => { // The results of this query will be cached like a normal query - await queryClient.prefetchQuery({ - queryKey: ['todos'], - queryFn: fetchTodos, - }) + await queryClient + .query({ + queryKey: ['todos'], + queryFn: fetchTodos, + }) + .catch(noop) } ``` [//]: # 'ExamplePrefetching' - If **fresh** data for this query is already in the cache, the data will not be fetched -- If a `staleTime` is passed eg. `prefetchQuery({ queryKey: ['todos'], queryFn: fn, staleTime: 5000 })` and the data is older than the specified `staleTime`, the query will be fetched +- If a `staleTime` is passed e.g. `queryClient.query({ queryKey: ['todos'], queryFn: fn, staleTime: 5000 })` and the data is older than the specified `staleTime`, the query will be fetched +- As `useQuery` will retry fetches and handle errors, you can use `void` to ignore the promise from `query` and `.catch(noop)` to ignore errors. +- If you want to always return cached data when it exists, use `staleTime: 'static'` - If no instances of `useQuery` appear for a prefetched query, it will be deleted and garbage collected after the time specified in `gcTime`. ## Prefetching Infinite Queries @@ -30,15 +36,19 @@ Infinite Queries can be prefetched like regular Queries. Per default, only the f [//]: # 'ExampleInfiniteQuery' ```tsx +import { noop } from '@tanstack/vue-query' + const prefetchProjects = async () => { // The results of this query will be cached like a normal query - await queryClient.prefetchInfiniteQuery({ - queryKey: ['projects'], - queryFn: fetchProjects, - initialPageParam: 0, - getNextPageParam: (lastPage, pages) => lastPage.nextCursor, - pages: 3, // prefetch the first 3 pages - }) + await queryClient + .infiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + pages: 3, // prefetch the first 3 pages + }) + .catch(noop) } ``` diff --git a/docs/framework/vue/guides/ssr.md b/docs/framework/vue/guides/ssr.md index 0170f71581..c6171c8aa1 100644 --- a/docs/framework/vue/guides/ssr.md +++ b/docs/framework/vue/guides/ssr.md @@ -50,12 +50,13 @@ export default defineNuxtPlugin((nuxt) => { Now you are ready to prefetch some data in your pages with `onServerPrefetch`. -- Prefetch all the queries that you need with `queryClient.prefetchQuery` or `suspense` +- Prefetch all the queries that you need with `queryClient.query`, `queryClient.infiniteQuery`, or `suspense` ```ts export default defineComponent({ setup() { - const { data, suspense } = useQuery({ + const queryClient = useQueryClient() + const { data } = useQuery({ queryKey: ['test'], queryFn: fetcher, }) @@ -110,7 +111,7 @@ Now you are ready to prefetch some data in your pages with `onServerPrefetch`. - Use `useContext` to get nuxt context - Use `useQueryClient` to get server-side instance of `queryClient` -- Prefetch all the queries that you need with `queryClient.prefetchQuery` or `suspense` +- Prefetch all the queries that you need with `queryClient.query`, `queryClient.infiniteQuery`, or `suspense` - Dehydrate `queryClient` to the `nuxtContext` ```vue @@ -169,7 +170,7 @@ export default defineComponent({ ``` -As demonstrated, it's fine to prefetch some queries and let others fetch on the queryClient. This means you can control what content server renders or not by adding or removing `prefetchQuery` or `suspense` for a specific query. +As demonstrated, it's fine to prefetch some queries and let others fetch on the client. This means you can control what content server renders or not by adding or removing `queryClient.query` or `suspense` for a specific query. ## Using Vite SSR @@ -237,7 +238,7 @@ Then, call VueQuery from any component using Vue's `onServerPrefetch`: Any query with an error is automatically excluded from dehydration. This means that the default behavior is to pretend these queries were never loaded on the server, usually showing a loading state instead, and retrying the queries on the queryClient. This happens regardless of error. -Sometimes this behavior is not desirable, maybe you want to render an error page with a correct status code instead on certain errors or queries. In those cases, use `fetchQuery` and catch any errors to handle those manually. +Sometimes this behavior is not desirable, maybe you want to render an error page with a correct status code instead on certain errors or queries. In those cases, use `queryClient.query` and catch any errors to handle those manually. ### Staleness is measured from when the query was fetched on the server diff --git a/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts b/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts index 49b5897b38..09550310a4 100644 --- a/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts +++ b/packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts @@ -58,6 +58,39 @@ 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({ + ...options, + 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({ + ...options, + 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 cd3af0d561..ff7e2d3fac 100644 --- a/packages/vue-query/src/__tests__/queryClient.test-d.ts +++ b/packages/vue-query/src/__tests__/queryClient.test-d.ts @@ -151,3 +151,107 @@ describe('fetchInfiniteQuery', () => { ]) }) }) + +describe('query', () => { + it('should return the type of the query fn', () => { + const result = new QueryClient().query({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + }) + + expectTypeOf(result).toEqualTypeOf>() + }) + + 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 not accept top-level options getters', () => { + assertType>([ + // @ts-expect-error One-shot imperative methods do not resolve top-level getters + () => ({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + }), + ]) + }) +}) + +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 not accept top-level options getters', () => { + assertType>([ + // @ts-expect-error One-shot imperative methods do not resolve top-level getters + () => ({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + getNextPageParam: () => 1, + initialPageParam: 1, + }), + ]) + }) + + 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 61f17739f9..c126ebabfb 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,51 @@ describe('QueryCache', () => { }) }) + describe('query', () => { + it('should properly unwrap queryKey', () => { + const queryClient = new QueryClient() + + queryClient.query({ + queryKey: queryKeyRef, + }) + + expect(QueryClientOrigin.prototype.query).toHaveBeenCalledWith({ + queryKey: queryKeyUnref, + }) + }) + + it('should properly unwrap staleTime, and select', () => { + const queryClient = new QueryClient() + const staleTime = () => 1000 + const select = (data: string) => data.length + + queryClient.query({ + queryKey: queryKeyRef, + staleTime: ref(staleTime), + select: ref(select), + }) + + expect(QueryClientOrigin.prototype.query).toHaveBeenCalledWith({ + queryKey: queryKeyUnref, + staleTime, + select, + }) + }) + + it('should accept explicitly resolved getter options and unwrap queryKey', () => { + const queryClient = new QueryClient() + const options = queryOptions(() => ({ + queryKey: queryKeyRef, + })) + + queryClient.query(options()) + + expect(QueryClientOrigin.prototype.query).toHaveBeenCalledWith({ + queryKey: queryKeyUnref, + }) + }) + }) + describe('prefetchQuery', () => { it('should properly unwrap parameters', () => { const queryClient = new QueryClient() @@ -393,6 +439,58 @@ 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), + staleTime: 0, + pages: 1, + }) + + expect(QueryClientOrigin.prototype.infiniteQuery).toHaveBeenCalledWith( + 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 2a9361452e..8b1ee84c1e 100644 --- a/packages/vue-query/src/__tests__/queryOptions.test-d.ts +++ b/packages/vue-query/src/__tests__/queryOptions.test-d.ts @@ -50,6 +50,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({ diff --git a/packages/vue-query/src/__tests__/usePrefetchInfiniteQuery.test.ts b/packages/vue-query/src/__tests__/usePrefetchInfiniteQuery.test.ts index 2456fa9e62..08777d53bd 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.ts b/packages/vue-query/src/__tests__/usePrefetchQuery.test.ts index 4cfcf832c4..87d7861cdf 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 9a8c56b787..353aca4eb7 100644 --- a/packages/vue-query/src/queryClient.ts +++ b/packages/vue-query/src/queryClient.ts @@ -15,12 +15,14 @@ import type { FetchQueryOptions, InferDataFromTag, InfiniteData, + InfiniteQueryExecuteOptions, InvalidateOptions, InvalidateQueryFilters, MutationFilters, MutationKey, MutationObserverOptions, OmitKeyof, + QueryExecuteOptions, QueryFilters, QueryKey, QueryObserverOptions, @@ -64,6 +66,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, @@ -231,6 +236,69 @@ export class QueryClient extends QC { ) } + // These one-shot imperative methods do not resolve top-level option getters. + // Resolve getters explicitly before calling, e.g. queryClient.query(options()). + 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 + > + >, + ): Promise + query< + TQueryFnData, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = never, + >( + options: MaybeRefDeep< + QueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam + > + >, + ): Promise { + return super.query(cloneDeepUnref(options)) + } + + /** + * @deprecated Use queryClient.query(options) instead. This method will be removed in the next major version. + */ fetchQuery< TQueryFnData, TError = DefaultError, @@ -279,6 +347,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, @@ -310,6 +381,75 @@ export class QueryClient extends QC { return super.prefetchQuery(cloneDeepUnref(options)) } + // These one-shot imperative methods do not resolve top-level option getters. + // Resolve getters explicitly before calling, e.g. queryClient.infiniteQuery(options()). + infiniteQuery< + TQueryFnData = unknown, + 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 = unknown, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + >( + options: MaybeRefDeep< + InfiniteQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + >, + ): Promise< + Array extends Array> + ? InfiniteData + : TData + > + infiniteQuery< + TQueryFnData = unknown, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, + >( + options: MaybeRefDeep< + InfiniteQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + >, + ): Promise< + Array extends Array> + ? InfiniteData + : TData + > { + return super.infiniteQuery(cloneDeepUnref(options)) + } + + /** + * @deprecated Use queryClient.infiniteQuery(options) instead. This method will be removed in the next major version. + */ fetchInfiniteQuery< TQueryFnData = unknown, TError = DefaultError, @@ -362,6 +502,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, diff --git a/packages/vue-query/src/usePrefetchInfiniteQuery.ts b/packages/vue-query/src/usePrefetchInfiniteQuery.ts index ff6ff23c0a..0520a7d50e 100644 --- a/packages/vue-query/src/usePrefetchInfiniteQuery.ts +++ b/packages/vue-query/src/usePrefetchInfiniteQuery.ts @@ -1,58 +1,45 @@ import { getCurrentScope, unref, watchEffect } from 'vue-demi' +import { noop } from '@tanstack/query-core' import { useQueryClient } from './useQueryClient' import { cloneDeepUnref } from './utils' import type { DefaultError, - FetchInfiniteQueryOptions, - FetchQueryOptions, - GetNextPageParamFunction, + DistributiveOmit, InfiniteData, - InitialPageParam, - OmitKeyof, + InfiniteQueryExecuteOptions, QueryKey, SkipToken, } 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 - } - export type UsePrefetchInfiniteQueryOptions< TQueryFnData, TError, TData, TQueryKey extends QueryKey, TPageParam, -> = OmitKeyof< - FetchQueryOptions< +> = DistributiveOmit< + InfiniteQueryExecuteOptions< TQueryFnData, TError, - InfiniteData, + TData, TQueryKey, TPageParam >, - 'queryFn' | 'initialPageParam' -> & - InitialPageParam & { - queryFn?: Exclude< - FetchQueryOptions< - TQueryFnData, - TError, - InfiniteData, - TQueryKey, - TPageParam - >['queryFn'], - SkipToken - > - } & PrefetchInfinitePages + 'queryFn' +> & { + queryFn?: Exclude< + InfiniteQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >['queryFn'], + SkipToken + > +} function isGetter(value: MaybeRefOrGetter): value is () => T { return typeof value === 'function' @@ -61,7 +48,7 @@ function isGetter(value: MaybeRefOrGetter): value is () => T { export function usePrefetchInfiniteQuery< TQueryFnData = unknown, TError = DefaultError, - TData = TQueryFnData, + TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( @@ -90,6 +77,7 @@ export function usePrefetchInfiniteQuery< watchEffect(() => { const resolvedOptions = isGetter(options) ? options() : unref(options) + const clonedOptions: UsePrefetchInfiniteQueryOptions< TQueryFnData, TError, @@ -99,15 +87,7 @@ export function usePrefetchInfiniteQuery< > = cloneDeepUnref(resolvedOptions) 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 85f239c54b..d8e230a6df 100644 --- a/packages/vue-query/src/usePrefetchQuery.ts +++ b/packages/vue-query/src/usePrefetchQuery.ts @@ -1,10 +1,11 @@ import { getCurrentScope, unref, watchEffect } from 'vue-demi' +import { noop } from '@tanstack/query-core' import { useQueryClient } from './useQueryClient' import { cloneDeepUnref } from './utils' import type { DefaultError, - FetchQueryOptions, OmitKeyof, + QueryExecuteOptions, QueryKey, SkipToken, } from '@tanstack/query-core' @@ -15,13 +16,28 @@ export type UsePrefetchQueryOptions< TQueryFnData, TError, TData, + TQueryData, TQueryKey extends QueryKey, > = OmitKeyof< - FetchQueryOptions, + QueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + never + >, 'queryFn' > & { queryFn?: Exclude< - FetchQueryOptions['queryFn'], + QueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + never + >['queryFn'], SkipToken > } @@ -34,11 +50,18 @@ export function usePrefetchQuery< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, + TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( options: MaybeRefOrGetter< MaybeRefDeep< - UsePrefetchQueryOptions + UsePrefetchQueryOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + > > >, queryClient?: QueryClient, @@ -59,11 +82,12 @@ export function usePrefetchQuery< TQueryFnData, TError, TData, + TQueryData, TQueryKey > = cloneDeepUnref(resolvedOptions) if (!client.getQueryState(clonedOptions.queryKey)) { - void client.prefetchQuery(clonedOptions) + void client.query(clonedOptions).catch(noop) } }) }