From 3d468d4351c25fb715d8efbc2b4f7bf52ce523fb Mon Sep 17 00:00:00 2001 From: Jonghyeon Ko Date: Wed, 28 Jan 2026 19:57:27 +0900 Subject: [PATCH 1/5] feat(react-query): backport usePrefetchQuery, usePrefetchInfiniteQuery --- .../usePrefetchInfiniteQuery.types.test.tsx | 40 +++++++++++++++ .../__tests__/usePrefetchQuery.types.test.tsx | 49 +++++++++++++++++++ packages/react-query/src/index.ts | 2 + .../src/usePrefetchInfiniteQuery.ts | 23 +++++++++ packages/react-query/src/usePrefetchQuery.ts | 23 +++++++++ 5 files changed, 137 insertions(+) create mode 100644 packages/react-query/src/__tests__/usePrefetchInfiniteQuery.types.test.tsx create mode 100644 packages/react-query/src/__tests__/usePrefetchQuery.types.test.tsx create mode 100644 packages/react-query/src/usePrefetchInfiniteQuery.ts create mode 100644 packages/react-query/src/usePrefetchQuery.ts diff --git a/packages/react-query/src/__tests__/usePrefetchInfiniteQuery.types.test.tsx b/packages/react-query/src/__tests__/usePrefetchInfiniteQuery.types.test.tsx new file mode 100644 index 00000000000..13ec51cc0c5 --- /dev/null +++ b/packages/react-query/src/__tests__/usePrefetchInfiniteQuery.types.test.tsx @@ -0,0 +1,40 @@ +import { expectTypeOf } from 'expect-type' +import { usePrefetchInfiniteQuery } from '../usePrefetchInfiniteQuery' + +describe('usePrefetchInfiniteQuery', () => { + it('should return nothing', () => { + const result = usePrefetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + getNextPageParam: () => 1, + }) + + expectTypeOf(result).toEqualTypeOf() + }) + + it('should not allow refetchInterval, enabled or throwOnError options', () => { + usePrefetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + getNextPageParam: () => 1, + // @ts-expect-error TS2353 + refetchInterval: 1000, + }) + + usePrefetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + getNextPageParam: () => 1, + // @ts-expect-error TS2353 + enabled: true, + }) + + usePrefetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + getNextPageParam: () => 1, + // @ts-expect-error TS2353 + throwOnError: true, + }) + }) +}) diff --git a/packages/react-query/src/__tests__/usePrefetchQuery.types.test.tsx b/packages/react-query/src/__tests__/usePrefetchQuery.types.test.tsx new file mode 100644 index 00000000000..101bc8166b2 --- /dev/null +++ b/packages/react-query/src/__tests__/usePrefetchQuery.types.test.tsx @@ -0,0 +1,49 @@ +import { expectTypeOf } from 'expect-type' +import { usePrefetchQuery } from '..' + +describe('usePrefetchQuery', () => { + it('should return nothing', () => { + const result = usePrefetchQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + }) + + expectTypeOf(result).toEqualTypeOf() + }) + + it('should not allow refetchInterval, enabled or throwOnError options', () => { + usePrefetchQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // @ts-expect-error TS2345 + refetchInterval: 1000, + }) + + usePrefetchQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // @ts-expect-error TS2345 + enabled: true, + }) + + usePrefetchQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // @ts-expect-error TS2345 + throwOnError: true, + }) + }) + + it('should not allow skipToken in queryFn', () => { + usePrefetchQuery({ + queryKey: ['key'], + // @ts-expect-error + queryFn: skipToken, + }) + usePrefetchQuery({ + queryKey: ['key'], + // @ts-expect-error + queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5), + }) + }) +}) diff --git a/packages/react-query/src/index.ts b/packages/react-query/src/index.ts index b4fbe8b403f..d04d91e70f3 100644 --- a/packages/react-query/src/index.ts +++ b/packages/react-query/src/index.ts @@ -46,3 +46,5 @@ export { useIsMutating } from './useIsMutating' export { useMutation } from './useMutation' export { useInfiniteQuery } from './useInfiniteQuery' export { useIsRestoring, IsRestoringProvider } from './isRestoring' +export { usePrefetchQuery } from './usePrefetchQuery' +export { usePrefetchInfiniteQuery } from './usePrefetchInfiniteQuery' diff --git a/packages/react-query/src/usePrefetchInfiniteQuery.ts b/packages/react-query/src/usePrefetchInfiniteQuery.ts new file mode 100644 index 00000000000..c7a51653121 --- /dev/null +++ b/packages/react-query/src/usePrefetchInfiniteQuery.ts @@ -0,0 +1,23 @@ +import { useQueryClient } from './QueryClientProvider' +import type { + FetchInfiniteQueryOptions, + QueryKey, + WithRequired, +} from '@tanstack/query-core' + +export function usePrefetchInfiniteQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +>( + options: WithRequired< + FetchInfiniteQueryOptions, + 'queryKey' + >, +) { + const client = useQueryClient() + if (!client.getQueryState(options.queryKey)) { + client.prefetchInfiniteQuery(options) + } +} diff --git a/packages/react-query/src/usePrefetchQuery.ts b/packages/react-query/src/usePrefetchQuery.ts new file mode 100644 index 00000000000..ba78a8b248d --- /dev/null +++ b/packages/react-query/src/usePrefetchQuery.ts @@ -0,0 +1,23 @@ +import { useQueryClient } from './QueryClientProvider' +import type { + FetchQueryOptions, + QueryKey, + WithRequired, +} from '@tanstack/query-core' + +export function usePrefetchQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +>( + options: WithRequired< + FetchQueryOptions, + 'queryKey' + >, +) { + const client = useQueryClient() + if (!client.getQueryState(options.queryKey)) { + client.prefetchQuery(options) + } +} From 11c56c39a43de962fefffaf18dff7bbc8573c806 Mon Sep 17 00:00:00 2001 From: Jonghyeon Ko Date: Wed, 28 Jan 2026 19:58:18 +0900 Subject: [PATCH 2/5] chore: add changeset for backporting usePrefetchQuery and usePrefetchInfiniteQuery --- .changeset/fast-snails-clap.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fast-snails-clap.md diff --git a/.changeset/fast-snails-clap.md b/.changeset/fast-snails-clap.md new file mode 100644 index 00000000000..294ebe9d141 --- /dev/null +++ b/.changeset/fast-snails-clap.md @@ -0,0 +1,5 @@ +--- +'@tanstack/react-query': minor +--- + +feat(react-query): backport usePrefetchQuery, usePrefetchInfiniteQuery From 2441f5aebae14f0d1450a4af33219ed5477da08b Mon Sep 17 00:00:00 2001 From: Jonghyeon Ko Date: Wed, 28 Jan 2026 20:03:21 +0900 Subject: [PATCH 3/5] test(react-query): wrap usePrefetchQuery and usePrefetchInfiniteQuery tests in doNotExecute utility to prevent execution --- .../usePrefetchInfiniteQuery.types.test.tsx | 57 ++++++++------- .../__tests__/usePrefetchQuery.types.test.tsx | 69 ++++++++++--------- 2 files changed, 69 insertions(+), 57 deletions(-) diff --git a/packages/react-query/src/__tests__/usePrefetchInfiniteQuery.types.test.tsx b/packages/react-query/src/__tests__/usePrefetchInfiniteQuery.types.test.tsx index 13ec51cc0c5..46fe41820b0 100644 --- a/packages/react-query/src/__tests__/usePrefetchInfiniteQuery.types.test.tsx +++ b/packages/react-query/src/__tests__/usePrefetchInfiniteQuery.types.test.tsx @@ -1,40 +1,45 @@ import { expectTypeOf } from 'expect-type' import { usePrefetchInfiniteQuery } from '../usePrefetchInfiniteQuery' +import { doNotExecute } from './utils' describe('usePrefetchInfiniteQuery', () => { it('should return nothing', () => { - const result = usePrefetchInfiniteQuery({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - getNextPageParam: () => 1, - }) + doNotExecute(() => { + const result = usePrefetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + getNextPageParam: () => 1, + }) - expectTypeOf(result).toEqualTypeOf() + expectTypeOf(result).toEqualTypeOf() + }) }) it('should not allow refetchInterval, enabled or throwOnError options', () => { - usePrefetchInfiniteQuery({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - getNextPageParam: () => 1, - // @ts-expect-error TS2353 - refetchInterval: 1000, - }) + doNotExecute(() => { + usePrefetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + getNextPageParam: () => 1, + // @ts-expect-error TS2353 + refetchInterval: 1000, + }) - usePrefetchInfiniteQuery({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - getNextPageParam: () => 1, - // @ts-expect-error TS2353 - enabled: true, - }) + usePrefetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + getNextPageParam: () => 1, + // @ts-expect-error TS2353 + enabled: true, + }) - usePrefetchInfiniteQuery({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - getNextPageParam: () => 1, - // @ts-expect-error TS2353 - throwOnError: true, + usePrefetchInfiniteQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + getNextPageParam: () => 1, + // @ts-expect-error TS2353 + throwOnError: true, + }) }) }) }) diff --git a/packages/react-query/src/__tests__/usePrefetchQuery.types.test.tsx b/packages/react-query/src/__tests__/usePrefetchQuery.types.test.tsx index 101bc8166b2..73b967c657e 100644 --- a/packages/react-query/src/__tests__/usePrefetchQuery.types.test.tsx +++ b/packages/react-query/src/__tests__/usePrefetchQuery.types.test.tsx @@ -1,49 +1,56 @@ import { expectTypeOf } from 'expect-type' import { usePrefetchQuery } from '..' +import { doNotExecute } from './utils' describe('usePrefetchQuery', () => { it('should return nothing', () => { - const result = usePrefetchQuery({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - }) + doNotExecute(() => { + const result = usePrefetchQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + }) - expectTypeOf(result).toEqualTypeOf() + expectTypeOf(result).toEqualTypeOf() + }) }) it('should not allow refetchInterval, enabled or throwOnError options', () => { - usePrefetchQuery({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - // @ts-expect-error TS2345 - refetchInterval: 1000, - }) + doNotExecute(() => { + usePrefetchQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // @ts-expect-error TS2345 + refetchInterval: 1000, + }) - usePrefetchQuery({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - // @ts-expect-error TS2345 - enabled: true, - }) + usePrefetchQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // @ts-expect-error TS2345 + enabled: true, + }) - usePrefetchQuery({ - queryKey: ['key'], - queryFn: () => Promise.resolve(5), - // @ts-expect-error TS2345 - throwOnError: true, + usePrefetchQuery({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // @ts-expect-error TS2345 + throwOnError: true, + }) }) }) it('should not allow skipToken in queryFn', () => { - usePrefetchQuery({ - queryKey: ['key'], - // @ts-expect-error - queryFn: skipToken, - }) - usePrefetchQuery({ - queryKey: ['key'], - // @ts-expect-error - queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5), + doNotExecute(() => { + usePrefetchQuery({ + queryKey: ['key'], + // @ts-expect-error + queryFn: skipToken, + }) + usePrefetchQuery({ + queryKey: ['key'], + // @ts-expect-error + queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5), + }) }) }) }) From a4f22dd143c5e27eb5e44070a67c1d9e707aa37b Mon Sep 17 00:00:00 2001 From: Jonghyeon Ko Date: Wed, 28 Jan 2026 20:12:01 +0900 Subject: [PATCH 4/5] chore: update changeset configuration to include fixed packages for @tanstack libraries --- .changeset/config.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.changeset/config.json b/.changeset/config.json index f509481bd38..1f433a84cf9 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -5,7 +5,20 @@ "access": "public", "baseBranch": "v4", "updateInternalDependencies": "patch", - "fixed": [], + "fixed": [ + "@tanstack/eslint-plugin-query", + "@tanstack/query-async-storage-persister", + "@tanstack/query-broadcast-client-experimental", + "@tanstack/query-core", + "@tanstack/query-persist-client-core", + "@tanstack/query-sync-storage-persister", + "@tanstack/react-query", + "@tanstack/react-query-devtools", + "@tanstack/react-query-persist-client", + "@tanstack/solid-query", + "@tanstack/svelte-query", + "@tanstack/vue-query" + ], "linked": [], "ignore": [], "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { From 60f636b51e9e23dd940c4011050b0a271c0dff85 Mon Sep 17 00:00:00 2001 From: Jonghyeon Ko Date: Wed, 28 Jan 2026 20:15:53 +0900 Subject: [PATCH 5/5] chore: refactor changeset configuration to group fixed packages for @tanstack libraries --- .changeset/config.json | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/.changeset/config.json b/.changeset/config.json index 1f433a84cf9..5df93c36140 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -6,18 +6,20 @@ "baseBranch": "v4", "updateInternalDependencies": "patch", "fixed": [ - "@tanstack/eslint-plugin-query", - "@tanstack/query-async-storage-persister", - "@tanstack/query-broadcast-client-experimental", - "@tanstack/query-core", - "@tanstack/query-persist-client-core", - "@tanstack/query-sync-storage-persister", - "@tanstack/react-query", - "@tanstack/react-query-devtools", - "@tanstack/react-query-persist-client", - "@tanstack/solid-query", - "@tanstack/svelte-query", - "@tanstack/vue-query" + [ + "@tanstack/eslint-plugin-query", + "@tanstack/query-async-storage-persister", + "@tanstack/query-broadcast-client-experimental", + "@tanstack/query-core", + "@tanstack/query-persist-client-core", + "@tanstack/query-sync-storage-persister", + "@tanstack/react-query", + "@tanstack/react-query-devtools", + "@tanstack/react-query-persist-client", + "@tanstack/solid-query", + "@tanstack/svelte-query", + "@tanstack/vue-query" + ] ], "linked": [], "ignore": [],