diff --git a/.changeset/warm-candies-like.md b/.changeset/warm-candies-like.md new file mode 100644 index 0000000000..66b15027ea --- /dev/null +++ b/.changeset/warm-candies-like.md @@ -0,0 +1,5 @@ +--- +'@tanstack/react-query': minor +--- + +react-query: update usePrefetchQuery and usePrefetchInfiniteQuery to use queryClient.query and queryClient.infiniteQuery diff --git a/docs/framework/react/reference/usePrefetchInfiniteQuery.md b/docs/framework/react/reference/usePrefetchInfiniteQuery.md index 1e86a35b55..497eb950d4 100644 --- a/docs/framework/react/reference/usePrefetchInfiniteQuery.md +++ b/docs/framework/react/reference/usePrefetchInfiniteQuery.md @@ -9,7 +9,7 @@ usePrefetchInfiniteQuery(options) **Options** -You can pass everything to `usePrefetchInfiniteQuery` that you can pass to [`queryClient.prefetchInfiniteQuery`](../../../reference/QueryClient.md#queryclientprefetchinfinitequery). Remember that some of them are required as below: +You can pass everything to `usePrefetchInfiniteQuery` that you can pass to [`queryClient.infiniteQuery`](../../../reference/QueryClient.md#queryclient-infinitequery). Remember that some of them are required as below: - `queryKey: QueryKey` - **Required** diff --git a/docs/framework/react/reference/usePrefetchQuery.md b/docs/framework/react/reference/usePrefetchQuery.md index 7feccad5fc..593afd350f 100644 --- a/docs/framework/react/reference/usePrefetchQuery.md +++ b/docs/framework/react/reference/usePrefetchQuery.md @@ -9,13 +9,13 @@ usePrefetchQuery(options) **Options** -You can pass everything to `usePrefetchQuery` that you can pass to [`queryClient.prefetchQuery`](../../../reference/QueryClient.md#queryclientprefetchquery). Remember that some of them are required as below: +You can pass everything to `usePrefetchQuery` that you can pass to [`queryClient.query`](../../../reference/QueryClient.md#queryclient-query). Remember that some of them are required as below: - `queryKey: QueryKey` - **Required** - The query key to prefetch during render -- `queryFn: (context: QueryFunctionContext) => Promise` +- `queryFn: (context: QueryFunctionContext) => TQueryFnData | Promise` - **Required, but only if no default query function has been defined** See [Default Query Function](../guides/default-query-function.md) for more information. **Returns** diff --git a/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx b/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx index ae160129ca..b0d233703a 100644 --- a/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx +++ b/packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx @@ -60,6 +60,20 @@ describe('infiniteQueryOptions', () => { InfiniteData | undefined >() }) + it('should work when passed to useInfiniteQuery with select', () => { + const options = infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + getNextPageParam: () => 1, + initialPageParam: 1, + select: (data) => data.pages, + }) + + const { data } = useInfiniteQuery(options) + + // known issue: type of pageParams is unknown when returned from useInfiniteQuery + expectTypeOf(data).toEqualTypeOf | undefined>() + }) it('should work when passed to useSuspenseInfiniteQuery', () => { const options = infiniteQueryOptions({ queryKey: queryKey(), @@ -72,6 +86,19 @@ describe('infiniteQueryOptions', () => { expectTypeOf(data).toEqualTypeOf>() }) + it('should work when passed to useSuspenseInfiniteQuery with select', () => { + const options = infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + getNextPageParam: () => 1, + initialPageParam: 1, + select: (data) => data.pages, + }) + + const { data } = useSuspenseInfiniteQuery(options) + + expectTypeOf(data).toEqualTypeOf>() + }) it('should work when passed to infiniteQuery', async () => { const options = infiniteQueryOptions({ queryKey: ['key'], @@ -134,6 +161,19 @@ describe('infiniteQueryOptions', () => { expectTypeOf(data).toEqualTypeOf>() }) + it('should ignore select when passed to fetchInfiniteQuery', async () => { + const options = infiniteQueryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve('string'), + getNextPageParam: () => 1, + initialPageParam: 1, + select: (data) => data.pages, + }) + + const data = await new QueryClient().fetchInfiniteQuery(options) + + expectTypeOf(data).toEqualTypeOf>() + }) it('should tag the queryKey with the result type of the QueryFn', () => { const { queryKey: tagged } = infiniteQueryOptions({ queryKey: queryKey(), diff --git a/packages/react-query/src/__tests__/queryOptions.test-d.tsx b/packages/react-query/src/__tests__/queryOptions.test-d.tsx index a818a00e45..fb67f06906 100644 --- a/packages/react-query/src/__tests__/queryOptions.test-d.tsx +++ b/packages/react-query/src/__tests__/queryOptions.test-d.tsx @@ -112,6 +112,16 @@ describe('queryOptions', () => { const data = await new QueryClient().query(options) expectTypeOf(data).toEqualTypeOf() }) + it('should ignore select when passed to fetchQuery', async () => { + const options = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + select: (data) => data.toString(), + }) + + const data = await new QueryClient().fetchQuery(options) + expectTypeOf(data).toEqualTypeOf() + }) it('should work when passed to useQueries', () => { const options = queryOptions({ queryKey: queryKey(), diff --git a/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx b/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx index ae272d8523..977cc53d76 100644 --- a/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx +++ b/packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx @@ -39,6 +39,18 @@ describe('pageParam', () => { }) }) + it('initialPageParam should define type of param passed to queryFunctionContext for infiniteQuery', () => { + const queryClient = new QueryClient() + queryClient.infiniteQuery({ + queryKey: ['key'], + queryFn: ({ pageParam }) => { + expectTypeOf(pageParam).toEqualTypeOf() + return Promise.resolve(pageParam) + }, + initialPageParam: 1, + }) + }) + it('initialPageParam should define type of param passed to queryFunctionContext for prefetchInfiniteQuery', () => { const queryClient = new QueryClient() queryClient.prefetchInfiniteQuery({ diff --git a/packages/react-query/src/__tests__/usePrefetchInfiniteQuery.test-d.tsx b/packages/react-query/src/__tests__/usePrefetchInfiniteQuery.test-d.tsx index 231c47cedb..ace4904a3e 100644 --- a/packages/react-query/src/__tests__/usePrefetchInfiniteQuery.test-d.tsx +++ b/packages/react-query/src/__tests__/usePrefetchInfiniteQuery.test-d.tsx @@ -1,6 +1,6 @@ import { assertType, describe, expectTypeOf, it } from 'vitest' import { queryKey } from '@tanstack/query-test-utils' -import { usePrefetchInfiniteQuery } from '..' +import { skipToken, usePrefetchInfiniteQuery } from '..' describe('usePrefetchInfiniteQuery', () => { it('should return nothing', () => { @@ -58,4 +58,25 @@ describe('usePrefetchInfiniteQuery', () => { }), ) }) + + it('should not allow skipToken in queryFn', () => { + assertType( + usePrefetchInfiniteQuery({ + queryKey: queryKey(), + // @ts-expect-error + queryFn: skipToken, + initialPageParam: 1, + getNextPageParam: () => 1, + }), + ) + assertType( + usePrefetchInfiniteQuery({ + queryKey: queryKey(), + // @ts-expect-error + queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5), + initialPageParam: 1, + getNextPageParam: () => 1, + }), + ) + }) }) diff --git a/packages/react-query/src/__tests__/usePrefetchQuery.test-d.tsx b/packages/react-query/src/__tests__/usePrefetchQuery.test-d.tsx index d5c1d7c235..08e878d1d4 100644 --- a/packages/react-query/src/__tests__/usePrefetchQuery.test-d.tsx +++ b/packages/react-query/src/__tests__/usePrefetchQuery.test-d.tsx @@ -57,4 +57,16 @@ describe('usePrefetchQuery', () => { }), ) }) + + it('should type queryFn and select data independently', () => { + usePrefetchQuery({ + queryKey: queryKey(), + queryFn: () => Promise.resolve('data'), + select: (data) => { + expectTypeOf(data).toEqualTypeOf<{ value: string }>() + + return data.value.length + }, + }) + }) }) diff --git a/packages/react-query/src/types.ts b/packages/react-query/src/types.ts index 43663ea559..bf72c8fc72 100644 --- a/packages/react-query/src/types.ts +++ b/packages/react-query/src/types.ts @@ -5,8 +5,8 @@ import type { DefinedInfiniteQueryObserverResult, DefinedQueryObserverResult, DistributiveOmit, - FetchQueryOptions, InfiniteData, + InfiniteQueryExecuteOptions, InfiniteQueryObserverOptions, InfiniteQueryObserverResult, MutateFunction, @@ -14,6 +14,7 @@ import type { MutationObserverResult, OmitKeyof, Override, + QueryExecuteOptions, QueryKey, QueryObserverOptions, QueryObserverResult, @@ -47,17 +48,52 @@ export interface UseBaseQueryOptions< subscribed?: boolean } -export interface UsePrefetchQueryOptions< +export type UsePrefetchQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, + TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> extends OmitKeyof< - FetchQueryOptions, +> = DistributiveOmit< + QueryExecuteOptions, 'queryFn' -> { +> & { + queryFn?: Exclude< + QueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >['queryFn'], + SkipToken + > +} + +export type UsePrefetchInfiniteQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = DistributiveOmit< + InfiniteQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, + 'queryFn' +> & { queryFn?: Exclude< - FetchQueryOptions['queryFn'], + InfiniteQueryExecuteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >['queryFn'], SkipToken > } diff --git a/packages/react-query/src/usePrefetchInfiniteQuery.tsx b/packages/react-query/src/usePrefetchInfiniteQuery.tsx index 08c2fcdfa3..1cd5a3dff2 100644 --- a/packages/react-query/src/usePrefetchInfiniteQuery.tsx +++ b/packages/react-query/src/usePrefetchInfiniteQuery.tsx @@ -1,19 +1,22 @@ +import { noop } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' + import type { DefaultError, - FetchInfiniteQueryOptions, + InfiniteData, QueryClient, QueryKey, } from '@tanstack/query-core' +import type { UsePrefetchInfiniteQueryOptions } from './types' export function usePrefetchInfiniteQuery< TQueryFnData = unknown, TError = DefaultError, - TData = TQueryFnData, + TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, >( - options: FetchInfiniteQueryOptions< + options: UsePrefetchInfiniteQueryOptions< TQueryFnData, TError, TData, @@ -21,10 +24,10 @@ export function usePrefetchInfiniteQuery< TPageParam >, queryClient?: QueryClient, -) { +): void { const client = useQueryClient(queryClient) if (!client.getQueryState(options.queryKey)) { - client.prefetchInfiniteQuery(options) + void client.infiniteQuery(options).catch(noop) } } diff --git a/packages/react-query/src/usePrefetchQuery.tsx b/packages/react-query/src/usePrefetchQuery.tsx index 3f508c3324..1b0d5268ca 100644 --- a/packages/react-query/src/usePrefetchQuery.tsx +++ b/packages/react-query/src/usePrefetchQuery.tsx @@ -1,4 +1,6 @@ +import { noop } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' + import type { DefaultError, QueryClient, QueryKey } from '@tanstack/query-core' import type { UsePrefetchQueryOptions } from './types' @@ -6,14 +8,21 @@ export function usePrefetchQuery< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, + TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: UsePrefetchQueryOptions, + options: UsePrefetchQueryOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >, queryClient?: QueryClient, -) { +): void { const client = useQueryClient(queryClient) if (!client.getQueryState(options.queryKey)) { - client.prefetchQuery(options) + void client.query(options).catch(noop) } }