Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9aaf337

Browse files
committed
feat(react-query): introduce infinite query options and suspense support
- Added `infiniteQueryOptions` function to manage infinite query configurations. - Implemented `useSuspenseInfiniteQuery` hook for handling infinite queries with suspense. - Updated types to include `DefinedUseInfiniteQueryResult` and `UseSuspenseInfiniteQueryResult`. - Marked deprecated overloads in `useQuery` and `useInfiniteQuery` for future removal. - Added tests for new infinite query options and suspense functionality.
1 parent 7e5e9f5 commit 9aaf337

13 files changed

+632
-40
lines changed

‎packages/query-core/src/types.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,11 +568,17 @@ export interface InfiniteQueryObserverSuccessResult<
568568
status: 'success'
569569
}
570570

571+
export type DefinedInfiniteQueryObserverResult<
572+
TData = unknown,
573+
TError = unknown,
574+
> =
575+
| InfiniteQueryObserverRefetchErrorResult<TData, TError>
576+
| InfiniteQueryObserverSuccessResult<TData, TError>
577+
571578
export type InfiniteQueryObserverResult<TData = unknown, TError = unknown> =
579+
| DefinedInfiniteQueryObserverResult<TData, TError>
572580
| InfiniteQueryObserverLoadingErrorResult<TData, TError>
573581
| InfiniteQueryObserverLoadingResult<TData, TError>
574-
| InfiniteQueryObserverRefetchErrorResult<TData, TError>
575-
| InfiniteQueryObserverSuccessResult<TData, TError>
576582

577583
export type MutationKey = readonly unknown[]
578584

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { infiniteQueryOptions } from '../infiniteQueryOptions'
2+
3+
describe('infiniteQueryOptions', () => {
4+
it('should return the object received as a parameter without any modification.', () => {
5+
const object = {
6+
queryKey: ['key'],
7+
queryFn: () => Promise.resolve(5),
8+
getNextPageParam: () => null,
9+
} as const
10+
11+
expect(infiniteQueryOptions(object)).toStrictEqual(object)
12+
})
13+
})
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { expectTypeOf } from 'expect-type'
2+
import {
3+
type InfiniteData,
4+
type UseInfiniteQueryResult,
5+
useInfiniteQuery,
6+
useQueryClient,
7+
} from '@tanstack/react-query'
8+
9+
import { useSuspenseInfiniteQuery } from '../useSuspenseInfiniteQuery'
10+
import { infiniteQueryOptions } from '../infiniteQueryOptions'
11+
import { doNotExecute } from './utils'
12+
import type {
13+
DefinedUseInfiniteQueryResult,
14+
UseSuspenseInfiniteQueryResult,
15+
} from '../types'
16+
17+
const infiniteQuery = {
18+
options: () =>
19+
infiniteQueryOptions({
20+
queryKey: ['key', 1] as const,
21+
queryFn: () => Promise.resolve({ field: 'success' }),
22+
}),
23+
optionsWithInitialData: () =>
24+
infiniteQueryOptions({
25+
queryKey: ['key', 2] as const,
26+
queryFn: () => Promise.resolve({ field: 'success' }),
27+
initialData: () => ({ pageParams: [], pages: [{ field: 'success' }] }),
28+
}),
29+
}
30+
31+
describe('infiniteQueryOptions', () => {
32+
it('should be used with useInfiniteQuery', () => {
33+
doNotExecute(() => {
34+
expectTypeOf(useInfiniteQuery(infiniteQuery.options())).toEqualTypeOf<
35+
UseInfiniteQueryResult<{ field: string }>
36+
>()
37+
38+
expectTypeOf(
39+
useInfiniteQuery({
40+
...infiniteQuery.options(),
41+
select: (data) => ({
42+
pages: data.pages.map(({ field }) => field),
43+
pageParams: data.pageParams,
44+
}),
45+
}),
46+
).toEqualTypeOf<UseInfiniteQueryResult<string>>()
47+
48+
expectTypeOf(
49+
useInfiniteQuery(infiniteQuery.optionsWithInitialData()),
50+
).toEqualTypeOf<DefinedUseInfiniteQueryResult<{ field: string }>>()
51+
52+
expectTypeOf(
53+
useInfiniteQuery({
54+
...infiniteQuery.optionsWithInitialData(),
55+
select: (data) => ({
56+
pages: data.pages.map(({ field }) => field),
57+
pageParams: data.pageParams,
58+
}),
59+
}),
60+
).toEqualTypeOf<DefinedUseInfiniteQueryResult<string>>()
61+
62+
expectTypeOf(
63+
useInfiniteQuery({
64+
queryKey: ['key', 2] as const,
65+
queryFn: () => Promise.resolve({ field: 'success' }),
66+
initialData: () => ({
67+
pages: [{ field: 'success' }],
68+
pageParams: [],
69+
}),
70+
select: (data) => ({
71+
pages: data.pages.map(({ field }) => field),
72+
pageParams: data.pageParams,
73+
}),
74+
}),
75+
).toEqualTypeOf<DefinedUseInfiniteQueryResult<string>>()
76+
})
77+
})
78+
it('should be used with useSuspenseInfiniteQuery', () => {
79+
doNotExecute(() => {
80+
expectTypeOf(
81+
useSuspenseInfiniteQuery(infiniteQuery.options()),
82+
).toEqualTypeOf<UseSuspenseInfiniteQueryResult<{ field: string }>>()
83+
84+
expectTypeOf(
85+
useSuspenseInfiniteQuery({
86+
...infiniteQuery.options(),
87+
select: (data) => ({
88+
pages: data.pages.map(({ field }) => field),
89+
pageParams: data.pageParams,
90+
}),
91+
}),
92+
).toEqualTypeOf<UseSuspenseInfiniteQueryResult<string>>()
93+
})
94+
})
95+
it('should be used with useQueryClient', () => {
96+
doNotExecute(async () => {
97+
const queryClient = useQueryClient()
98+
99+
queryClient.invalidateQueries(infiniteQuery.options())
100+
queryClient.resetQueries(infiniteQuery.options())
101+
queryClient.removeQueries(infiniteQuery.options())
102+
queryClient.cancelQueries(infiniteQuery.options())
103+
queryClient.prefetchQuery(infiniteQuery.options())
104+
queryClient.refetchQueries(infiniteQuery.options())
105+
106+
expectTypeOf(
107+
await queryClient.fetchQuery(infiniteQuery.options()),
108+
).toEqualTypeOf<InfiniteData<{ field: string }>>()
109+
})
110+
})
111+
})

‎packages/react-query/src/__tests__/queryOptions.types.test.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ const queryFn = () => Promise.resolve({ field: 'success' })
1919
describe('queryOptions', () => {
2020
it('should be used with useQuery', () => {
2121
doNotExecute(() => {
22-
const dd = useQuery(
23-
queryOptions({
24-
queryKey,
25-
queryFn,
26-
}),
27-
)
28-
expectTypeOf(dd).toEqualTypeOf<UseQueryResult<{ field: string }>>()
22+
expectTypeOf(
23+
useQuery(
24+
queryOptions({
25+
queryKey,
26+
queryFn,
27+
}),
28+
),
29+
).toEqualTypeOf<UseQueryResult<{ field: string }>>()
2930
expectTypeOf(
3031
useQuery({
3132
...queryOptions({
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { expectTypeOf } from 'expect-type'
2+
import { infiniteQueryOptions, useSuspenseInfiniteQuery } from '..'
3+
import { doNotExecute, sleep } from './utils'
4+
import type { UseSuspenseInfiniteQueryResult } from '..'
5+
6+
import type { InfiniteData } from '@tanstack/react-query'
7+
8+
const queryKey = ['key'] as const
9+
const queryFn = () => sleep(10).then(() => ({ text: 'response' }))
10+
11+
describe('useSuspenseInfiniteQuery', () => {
12+
it('type check', () => {
13+
doNotExecute(() => {
14+
// @ts-expect-error no arg
15+
useSuspenseInfiniteQuery()
16+
17+
useSuspenseInfiniteQuery({
18+
queryKey,
19+
queryFn,
20+
// @ts-expect-error no suspense
21+
suspense: boolean,
22+
})
23+
useSuspenseInfiniteQuery({
24+
queryKey,
25+
queryFn,
26+
// @ts-expect-error no useErrorBoundary
27+
useErrorBoundary: boolean,
28+
})
29+
useSuspenseInfiniteQuery({
30+
queryKey,
31+
queryFn,
32+
// @ts-expect-error no enabled
33+
enabled: boolean,
34+
})
35+
useSuspenseInfiniteQuery({
36+
queryKey,
37+
queryFn,
38+
// @ts-expect-error no placeholderData
39+
placeholderData: 'placeholder',
40+
})
41+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
42+
useSuspenseInfiniteQuery({
43+
queryKey,
44+
queryFn,
45+
// @ts-expect-error no isPlaceholderData
46+
}).isPlaceholderData
47+
useSuspenseInfiniteQuery({
48+
queryKey,
49+
queryFn,
50+
//@ts-expect-error no networkMode
51+
networkMode: 'always',
52+
})
53+
54+
const infiniteQuery = useSuspenseInfiniteQuery({ queryKey, queryFn })
55+
expectTypeOf(infiniteQuery).toEqualTypeOf<
56+
UseSuspenseInfiniteQueryResult<{ text: string }>
57+
>()
58+
expectTypeOf(infiniteQuery.data).toEqualTypeOf<
59+
InfiniteData<{ text: string }>
60+
>()
61+
expectTypeOf(infiniteQuery.status).toEqualTypeOf<'error' | 'success'>()
62+
63+
const selectedInfiniteQuery = useSuspenseInfiniteQuery({
64+
queryKey,
65+
queryFn,
66+
select: (data) => ({
67+
pages: data.pages.map(({ text }) => text),
68+
pageParams: data.pageParams,
69+
}),
70+
})
71+
expectTypeOf(selectedInfiniteQuery).toEqualTypeOf<
72+
UseSuspenseInfiniteQueryResult<string>
73+
>()
74+
expectTypeOf(selectedInfiniteQuery.data).toEqualTypeOf<
75+
InfiniteData<string>
76+
>()
77+
expectTypeOf(selectedInfiniteQuery.status).toEqualTypeOf<
78+
'error' | 'success'
79+
>()
80+
81+
const options = infiniteQueryOptions({
82+
queryKey,
83+
queryFn,
84+
})
85+
86+
const infiniteQueryWithOptions = useSuspenseInfiniteQuery(options)
87+
expectTypeOf(infiniteQueryWithOptions).toEqualTypeOf<
88+
UseSuspenseInfiniteQueryResult<{ text: string }>
89+
>()
90+
expectTypeOf(infiniteQueryWithOptions.data).toEqualTypeOf<
91+
InfiniteData<{ text: string }>
92+
>()
93+
expectTypeOf(infiniteQueryWithOptions.status).toEqualTypeOf<
94+
'error' | 'success'
95+
>()
96+
97+
const selectedInfiniteQueryWithOptions = useSuspenseInfiniteQuery({
98+
...options,
99+
select: (data) => ({
100+
pages: data.pages.map(({ text }) => text),
101+
pageParams: data.pageParams,
102+
}),
103+
})
104+
expectTypeOf(selectedInfiniteQueryWithOptions).toEqualTypeOf<
105+
UseSuspenseInfiniteQueryResult<string>
106+
>()
107+
expectTypeOf(selectedInfiniteQueryWithOptions.data).toEqualTypeOf<
108+
InfiniteData<string>
109+
>()
110+
expectTypeOf(selectedInfiniteQueryWithOptions.status).toEqualTypeOf<
111+
'error' | 'success'
112+
>()
113+
})
114+
})
115+
})

‎packages/react-query/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export { useQueries } from './useQueries'
1212
export type { QueriesResults, QueriesOptions } from './useQueries'
1313
export { useQuery } from './useQuery'
1414
export { useSuspenseQuery } from './useSuspenseQuery'
15+
export { useSuspenseInfiniteQuery } from './useSuspenseInfiniteQuery'
1516
export { useSuspenseQueries } from './useSuspenseQueries'
1617
export type {
1718
SuspenseQueriesResults,
@@ -22,6 +23,11 @@ export type {
2223
DefinedInitialDataOptions,
2324
UndefinedInitialDataOptions,
2425
} from './queryOptions'
26+
export { infiniteQueryOptions } from './infiniteQueryOptions'
27+
export type {
28+
DefinedInitialDataInfiniteOptions,
29+
UndefinedInitialDataInfiniteOptions,
30+
} from './infiniteQueryOptions'
2531
export {
2632
defaultContext,
2733
QueryClientProvider,
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import type { UseInfiniteQueryOptions } from './types'
2+
import type {
3+
InfiniteData,
4+
NonUndefinedGuard,
5+
OmitKeyof,
6+
QueryKey,
7+
WithRequired,
8+
} from '@tanstack/query-core'
9+
10+
type UseInfiniteQueryOptionsOmitted<
11+
TQueryFnData = unknown,
12+
TError = unknown,
13+
TData = TQueryFnData,
14+
TQueryKey extends QueryKey = QueryKey,
15+
> = OmitKeyof<
16+
UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>,
17+
'onSuccess' | 'onError' | 'onSettled' | 'refetchInterval'
18+
>
19+
20+
type ProhibitedInfiniteQueryOptionsKeyInV5 = keyof Pick<
21+
UseInfiniteQueryOptionsOmitted,
22+
'useErrorBoundary' | 'suspense'
23+
>
24+
25+
export type UndefinedInitialDataInfiniteOptions<
26+
TQueryFnData,
27+
TError = unknown,
28+
TData = TQueryFnData,
29+
TQueryKey extends QueryKey = QueryKey,
30+
> = UseInfiniteQueryOptionsOmitted<TQueryFnData, TError, TData, TQueryKey> & {
31+
initialData?: undefined
32+
}
33+
34+
export type DefinedInitialDataInfiniteOptions<
35+
TQueryFnData,
36+
TError = unknown,
37+
TData = TQueryFnData,
38+
TQueryKey extends QueryKey = QueryKey,
39+
> = UseInfiniteQueryOptionsOmitted<TQueryFnData, TError, TData, TQueryKey> & {
40+
initialData:
41+
| NonUndefinedGuard<InfiniteData<TQueryFnData>>
42+
| (() => NonUndefinedGuard<InfiniteData<TQueryFnData>>)
43+
| undefined
44+
}
45+
46+
export function infiniteQueryOptions<
47+
TQueryFnData,
48+
TError = unknown,
49+
TData = TQueryFnData,
50+
TQueryKey extends QueryKey = QueryKey,
51+
>(
52+
options: WithRequired<
53+
OmitKeyof<
54+
DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey>,
55+
ProhibitedInfiniteQueryOptionsKeyInV5
56+
>,
57+
'queryKey'
58+
>,
59+
): WithRequired<
60+
OmitKeyof<
61+
DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey>,
62+
ProhibitedInfiniteQueryOptionsKeyInV5
63+
>,
64+
'queryKey'
65+
>
66+
67+
export function infiniteQueryOptions<
68+
TQueryFnData,
69+
TError = unknown,
70+
TData = TQueryFnData,
71+
TQueryKey extends QueryKey = QueryKey,
72+
>(
73+
options: WithRequired<
74+
OmitKeyof<
75+
UndefinedInitialDataInfiniteOptions<
76+
TQueryFnData,
77+
TError,
78+
TData,
79+
TQueryKey
80+
>,
81+
ProhibitedInfiniteQueryOptionsKeyInV5
82+
>,
83+
'queryKey'
84+
>,
85+
): WithRequired<
86+
OmitKeyof<
87+
UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey>,
88+
ProhibitedInfiniteQueryOptionsKeyInV5
89+
>,
90+
'queryKey'
91+
>
92+
93+
export function infiniteQueryOptions(options: unknown) {
94+
return options
95+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /