Skip to content

Commit 56487e6

Browse files
committed
feat(query-core, react-query): backport v5 apis(isPending, useSuspenseQuery, useSuspenseQueries, queryOptions) to v4
1 parent 4e6432a commit 56487e6

17 files changed

+2752
-185
lines changed

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v16.19.0
1+
22.12.0

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"eslint-plugin-import": "^2.27.5",
7676
"eslint-plugin-react": "^7.32.2",
7777
"eslint-plugin-react-hooks": "^4.6.0",
78+
"expect-type": "^1.2.1",
7879
"git-log-parser": "^1.2.0",
7980
"jest": "^27.5.1",
8081
"jsonfile": "^6.1.0",

packages/query-core/src/mutationObserver.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,16 @@ export class MutationObserver<
141141
? this.currentMutation.state
142142
: getDefaultState<TData, TError, TVariables, TContext>()
143143

144+
const isLoading = state.status === 'loading'
144145
const result: MutationObserverBaseResult<
145146
TData,
146147
TError,
147148
TVariables,
148149
TContext
149150
> = {
150151
...state,
151-
isLoading: state.status === 'loading',
152+
isLoading,
153+
isPending: isLoading,
152154
isSuccess: state.status === 'success',
153155
isError: state.status === 'error',
154156
isIdle: state.status === 'idle',

packages/query-core/src/tests/mutations.test.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ describe('mutations', () => {
7676
isError: false,
7777
isIdle: true,
7878
isLoading: false,
79+
isPending: false,
7980
isPaused: false,
8081
isSuccess: false,
8182
mutate: expect.any(Function),
@@ -103,6 +104,7 @@ describe('mutations', () => {
103104
isError: false,
104105
isIdle: false,
105106
isLoading: true,
107+
isPending: true,
106108
isPaused: false,
107109
isSuccess: false,
108110
mutate: expect.any(Function),
@@ -122,6 +124,7 @@ describe('mutations', () => {
122124
isError: false,
123125
isIdle: false,
124126
isLoading: true,
127+
isPending: true,
125128
isPaused: false,
126129
isSuccess: false,
127130
mutate: expect.any(Function),
@@ -141,6 +144,7 @@ describe('mutations', () => {
141144
isError: false,
142145
isIdle: false,
143146
isLoading: false,
147+
isPending: false,
144148
isPaused: false,
145149
isSuccess: true,
146150
mutate: expect.any(Function),
@@ -181,6 +185,7 @@ describe('mutations', () => {
181185
isError: false,
182186
isIdle: false,
183187
isLoading: true,
188+
isPending: true,
184189
isPaused: false,
185190
isSuccess: false,
186191
mutate: expect.any(Function),
@@ -200,6 +205,7 @@ describe('mutations', () => {
200205
isError: false,
201206
isIdle: false,
202207
isLoading: true,
208+
isPending: true,
203209
isPaused: false,
204210
isSuccess: false,
205211
mutate: expect.any(Function),
@@ -219,6 +225,7 @@ describe('mutations', () => {
219225
isError: false,
220226
isIdle: false,
221227
isLoading: true,
228+
isPending: true,
222229
isPaused: false,
223230
isSuccess: false,
224231
mutate: expect.any(Function),
@@ -238,6 +245,7 @@ describe('mutations', () => {
238245
isError: true,
239246
isIdle: false,
240247
isLoading: false,
248+
isPending: false,
241249
isPaused: false,
242250
isSuccess: false,
243251
mutate: expect.any(Function),

packages/query-core/src/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@ import type { QueryCache } from './queryCache'
88
import type { MutationCache } from './mutationCache'
99
import type { Logger } from './logger'
1010

11+
export type OmitKeyof<
12+
TObject,
13+
TKey extends TStrictly extends 'safely'
14+
?
15+
| keyof TObject
16+
| (string & Record<never, never>)
17+
| (number & Record<never, never>)
18+
| (symbol & Record<never, never>)
19+
: keyof TObject,
20+
TStrictly extends 'strictly' | 'safely' = 'strictly',
21+
> = Omit<TObject, TKey>
22+
1123
export type QueryKey = readonly unknown[]
1224

1325
export type QueryFunction<
@@ -256,6 +268,8 @@ export interface QueryObserverOptions<
256268
/**
257269
* Set this to `true` to keep the previous `data` when fetching based on a new query key.
258270
* Defaults to `false`.
271+
*
272+
* @deprecated keepPreviousData will be removed in the next major version.
259273
*/
260274
keepPreviousData?: boolean
261275
/**
@@ -646,6 +660,7 @@ export interface MutationObserverBaseResult<
646660
isError: boolean
647661
isIdle: boolean
648662
isLoading: boolean
663+
isPending: boolean
649664
isSuccess: boolean
650665
mutate: MutateFunction<TData, TError, TVariables, TContext>
651666
reset: () => void
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
import { expectTypeOf } from 'expect-type'
2+
import {
3+
type UseQueryResult,
4+
useQueries,
5+
useQuery,
6+
useQueryClient,
7+
} from '@tanstack/react-query'
8+
import { queryOptions } from '..'
9+
import { useSuspenseQueries, useSuspenseQuery } from '..'
10+
import { type UseSuspenseQueryResult } from '../useSuspenseQuery'
11+
import { doNotExecute } from './utils'
12+
import type { DefinedUseQueryResult } from '@tanstack/react-query'
13+
14+
const queryKey = ['key'] as const
15+
const queryFn = () => Promise.resolve({ field: 'success' })
16+
17+
describe('queryOptions', () => {
18+
it('should be used with useQuery', () => {
19+
doNotExecute(() => {
20+
const dd = useQuery(
21+
queryOptions({
22+
queryKey,
23+
queryFn,
24+
}),
25+
)
26+
expectTypeOf(dd).toEqualTypeOf<UseQueryResult<{ field: string }>>()
27+
expectTypeOf(
28+
useQuery({
29+
...queryOptions({
30+
queryKey,
31+
queryFn,
32+
}),
33+
select: (data) => data.field,
34+
}),
35+
).toEqualTypeOf<UseQueryResult<string>>()
36+
expectTypeOf(
37+
useQuery({
38+
...queryOptions({
39+
queryKey,
40+
queryFn,
41+
select: (data) => data.field,
42+
}),
43+
}),
44+
).toEqualTypeOf<UseQueryResult<string>>()
45+
expectTypeOf(
46+
useQuery({
47+
...queryOptions({
48+
queryKey,
49+
queryFn,
50+
}),
51+
initialData: { field: 'success' },
52+
}),
53+
).toEqualTypeOf<DefinedUseQueryResult<{ field: string }>>()
54+
expectTypeOf(
55+
useQuery({
56+
...queryOptions({
57+
queryKey,
58+
queryFn,
59+
}),
60+
initialData: { field: 'success' },
61+
select: (data) => data.field,
62+
}),
63+
).toEqualTypeOf<DefinedUseQueryResult<string>>()
64+
expectTypeOf(
65+
useQuery({
66+
...queryOptions({
67+
queryKey,
68+
queryFn,
69+
}),
70+
initialData: undefined,
71+
select: (data) => data.field,
72+
}),
73+
).toEqualTypeOf<UseQueryResult<string>>()
74+
expectTypeOf(
75+
useQuery({
76+
...queryOptions({
77+
queryKey,
78+
queryFn,
79+
}),
80+
initialData: () => undefined,
81+
select: (data) => data.field,
82+
}),
83+
).toEqualTypeOf<UseQueryResult<string>>()
84+
expectTypeOf(
85+
useQuery({
86+
...queryOptions({
87+
queryKey,
88+
queryFn,
89+
select: (data) => data.field,
90+
}),
91+
refetchInterval: 1000,
92+
}),
93+
).toEqualTypeOf<UseQueryResult<string>>()
94+
})
95+
})
96+
it('should be used with useSuspenseQuery', () => {
97+
doNotExecute(() => {
98+
expectTypeOf(
99+
useSuspenseQuery(
100+
queryOptions({
101+
queryKey,
102+
queryFn,
103+
}),
104+
),
105+
).toEqualTypeOf<UseSuspenseQueryResult<{ field: string }>>()
106+
107+
expectTypeOf(
108+
useSuspenseQuery({
109+
...queryOptions({
110+
queryKey,
111+
queryFn,
112+
}),
113+
select: (data) => data.field,
114+
}),
115+
).toEqualTypeOf<UseSuspenseQueryResult<string>>()
116+
expectTypeOf(
117+
useSuspenseQuery({
118+
...queryOptions({
119+
queryKey,
120+
queryFn,
121+
}),
122+
initialData: { field: 'success' },
123+
}),
124+
).toEqualTypeOf<UseSuspenseQueryResult<{ field: string }>>()
125+
expectTypeOf(
126+
useSuspenseQuery({
127+
...queryOptions({
128+
queryKey,
129+
queryFn,
130+
}),
131+
initialData: undefined,
132+
select: (data) => data.field,
133+
}),
134+
).toEqualTypeOf<UseSuspenseQueryResult<string>>()
135+
expectTypeOf(
136+
useSuspenseQuery({
137+
...queryOptions({
138+
queryKey,
139+
queryFn,
140+
}),
141+
initialData: { field: 'success' },
142+
select: (data) => data.field,
143+
}),
144+
).toEqualTypeOf<UseSuspenseQueryResult<string>>()
145+
expectTypeOf(
146+
useSuspenseQuery({
147+
...queryOptions({
148+
queryKey,
149+
queryFn,
150+
select: (data) => data.field,
151+
}),
152+
refetchInterval: 1000,
153+
}),
154+
).toEqualTypeOf<UseSuspenseQueryResult<string>>()
155+
})
156+
})
157+
it('should be used with useQueries', () => {
158+
doNotExecute(() => {
159+
const [query1, query2, query3, query4] = useQueries({
160+
queries: [
161+
queryOptions({
162+
queryKey,
163+
queryFn,
164+
}),
165+
queryOptions({
166+
queryKey,
167+
queryFn,
168+
initialData: { field: 'success' },
169+
}),
170+
{
171+
...queryOptions({
172+
queryKey,
173+
queryFn,
174+
}),
175+
initialData: { field: 'success' },
176+
},
177+
{
178+
...queryOptions({
179+
queryKey,
180+
queryFn,
181+
}),
182+
initialData: undefined,
183+
},
184+
],
185+
})
186+
expectTypeOf(query1).toEqualTypeOf<UseQueryResult<{ field: string }>>()
187+
expectTypeOf(query2).toEqualTypeOf<
188+
DefinedUseQueryResult<{ field: string }>
189+
>()
190+
expectTypeOf(query3).toEqualTypeOf<
191+
DefinedUseQueryResult<{ field: string }>
192+
>()
193+
expectTypeOf(query4).toEqualTypeOf<UseQueryResult<{ field: string }>>()
194+
})
195+
})
196+
it('should be used with useSuspenseQueries', () => {
197+
doNotExecute(() => {
198+
const [query1, query2, query3, query4, query5] = useSuspenseQueries({
199+
queries: [
200+
queryOptions({
201+
queryKey,
202+
queryFn,
203+
}),
204+
queryOptions({
205+
queryKey,
206+
queryFn,
207+
initialData: { field: 'success' },
208+
}),
209+
{
210+
...queryOptions({
211+
queryKey,
212+
queryFn,
213+
}),
214+
initialData: { field: 'success' },
215+
},
216+
{
217+
...queryOptions({
218+
queryKey,
219+
queryFn,
220+
}),
221+
initialData: undefined,
222+
},
223+
{
224+
...queryOptions({
225+
queryKey,
226+
queryFn,
227+
select: (data) => data.field,
228+
}),
229+
},
230+
],
231+
})
232+
expectTypeOf(query1).toEqualTypeOf<
233+
UseSuspenseQueryResult<{ field: string }>
234+
>()
235+
expectTypeOf(query2).toEqualTypeOf<
236+
UseSuspenseQueryResult<{ field: string }>
237+
>()
238+
expectTypeOf(query3).toEqualTypeOf<
239+
UseSuspenseQueryResult<{ field: string }>
240+
>()
241+
expectTypeOf(query4).toEqualTypeOf<
242+
UseSuspenseQueryResult<{ field: string }>
243+
>()
244+
expectTypeOf(query5).toEqualTypeOf<UseSuspenseQueryResult<string>>()
245+
})
246+
})
247+
it('should be used with useQueryClient', () => {
248+
doNotExecute(async () => {
249+
const queryClient = useQueryClient()
250+
queryClient.invalidateQueries(queryOptions({ queryKey, queryFn }))
251+
queryClient.resetQueries(queryOptions({ queryKey, queryFn }))
252+
queryClient.removeQueries(queryOptions({ queryKey, queryFn }))
253+
queryClient.cancelQueries(queryOptions({ queryKey, queryFn }))
254+
queryClient.prefetchQuery(queryOptions({ queryKey, queryFn }))
255+
queryClient.refetchQueries(queryOptions({ queryKey, queryFn }))
256+
expectTypeOf(
257+
await queryClient.fetchQuery(queryOptions({ queryKey, queryFn })),
258+
).toEqualTypeOf<{ field: string }>()
259+
})
260+
})
261+
})

0 commit comments

Comments
 (0)