Skip to content

Commit 208029e

Browse files
authored
feat(react-query): backport v5 apis about infinite query (#9334)
1 parent 508d5a0 commit 208029e

12 files changed

+474
-18
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)