@tanstack/react-query 4.40.1 → 4.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/build/lib/__tests__/infiniteQueryOptions.test.d.ts +2 -0
  2. package/build/lib/__tests__/infiniteQueryOptions.types.test.d.ts +2 -0
  3. package/build/lib/__tests__/useSuspenseInfiniteQuery.types.test.d.ts +2 -0
  4. package/build/lib/index.d.ts +3 -0
  5. package/build/lib/index.esm.js +2 -0
  6. package/build/lib/index.esm.js.map +1 -1
  7. package/build/lib/index.js +4 -0
  8. package/build/lib/index.js.map +1 -1
  9. package/build/lib/index.mjs +2 -0
  10. package/build/lib/index.mjs.map +1 -1
  11. package/build/lib/infiniteQueryOptions.d.ts +14 -0
  12. package/build/lib/infiniteQueryOptions.esm.js +6 -0
  13. package/build/lib/infiniteQueryOptions.esm.js.map +1 -0
  14. package/build/lib/infiniteQueryOptions.js +10 -0
  15. package/build/lib/infiniteQueryOptions.js.map +1 -0
  16. package/build/lib/infiniteQueryOptions.mjs +6 -0
  17. package/build/lib/infiniteQueryOptions.mjs.map +1 -0
  18. package/build/lib/queryOptions.d.ts +2 -2
  19. package/build/lib/queryOptions.esm.js.map +1 -1
  20. package/build/lib/queryOptions.js.map +1 -1
  21. package/build/lib/queryOptions.mjs.map +1 -1
  22. package/build/lib/suspense.d.ts +7 -0
  23. package/build/lib/suspense.esm.js +11 -0
  24. package/build/lib/suspense.esm.js.map +1 -1
  25. package/build/lib/suspense.js +11 -0
  26. package/build/lib/suspense.js.map +1 -1
  27. package/build/lib/suspense.mjs +11 -0
  28. package/build/lib/suspense.mjs.map +1 -1
  29. package/build/lib/types.d.ts +3 -1
  30. package/build/lib/useInfiniteQuery.d.ts +7 -2
  31. package/build/lib/useInfiniteQuery.esm.js.map +1 -1
  32. package/build/lib/useInfiniteQuery.js.map +1 -1
  33. package/build/lib/useInfiniteQuery.mjs.map +1 -1
  34. package/build/lib/useQuery.d.ts +6 -6
  35. package/build/lib/useQuery.esm.js.map +1 -1
  36. package/build/lib/useQuery.js.map +1 -1
  37. package/build/lib/useQuery.mjs.map +1 -1
  38. package/build/lib/useSuspenseInfiniteQuery.d.ts +5 -0
  39. package/build/lib/useSuspenseInfiniteQuery.esm.js +14 -0
  40. package/build/lib/useSuspenseInfiniteQuery.esm.js.map +1 -0
  41. package/build/lib/useSuspenseInfiniteQuery.js +18 -0
  42. package/build/lib/useSuspenseInfiniteQuery.js.map +1 -0
  43. package/build/lib/useSuspenseInfiniteQuery.mjs +14 -0
  44. package/build/lib/useSuspenseInfiniteQuery.mjs.map +1 -0
  45. package/build/umd/index.development.js +26 -0
  46. package/build/umd/index.development.js.map +1 -1
  47. package/build/umd/index.production.js +1 -1
  48. package/build/umd/index.production.js.map +1 -1
  49. package/package.json +2 -2
  50. package/src/__tests__/infiniteQueryOptions.test.tsx +13 -0
  51. package/src/__tests__/infiniteQueryOptions.types.test.tsx +111 -0
  52. package/src/__tests__/queryOptions.types.test.tsx +8 -7
  53. package/src/__tests__/suspense.test.tsx +230 -0
  54. package/src/__tests__/useSuspenseInfiniteQuery.types.test.tsx +115 -0
  55. package/src/index.ts +6 -0
  56. package/src/infiniteQueryOptions.ts +95 -0
  57. package/src/queryOptions.ts +8 -2
  58. package/src/suspense.ts +11 -0
  59. package/src/types.ts +14 -0
  60. package/src/useInfiniteQuery.ts +28 -1
  61. package/src/useQuery.ts +6 -6
  62. package/src/useSuspenseInfiniteQuery.ts +62 -0
  63. package/build/lib/Hydrate.d.ts.map +0 -1
  64. package/build/lib/QueryClientProvider.d.ts.map +0 -1
  65. package/build/lib/QueryErrorResetBoundary.d.ts.map +0 -1
  66. package/build/lib/__tests__/Hydrate.test.d.ts.map +0 -1
  67. package/build/lib/__tests__/QueryClientProvider.test.d.ts.map +0 -1
  68. package/build/lib/__tests__/QueryResetErrorBoundary.test.d.ts.map +0 -1
  69. package/build/lib/__tests__/queryOptions.types.test.d.ts.map +0 -1
  70. package/build/lib/__tests__/ssr-hydration.test.d.ts.map +0 -1
  71. package/build/lib/__tests__/ssr.test.d.ts.map +0 -1
  72. package/build/lib/__tests__/suspense.test.d.ts.map +0 -1
  73. package/build/lib/__tests__/useInfiniteQuery.test.d.ts.map +0 -1
  74. package/build/lib/__tests__/useIsFetching.test.d.ts.map +0 -1
  75. package/build/lib/__tests__/useIsMutating.test.d.ts.map +0 -1
  76. package/build/lib/__tests__/useMutation.test.d.ts.map +0 -1
  77. package/build/lib/__tests__/useQueries.test.d.ts.map +0 -1
  78. package/build/lib/__tests__/useQuery.test.d.ts.map +0 -1
  79. package/build/lib/__tests__/useQuery.types.test.d.ts.map +0 -1
  80. package/build/lib/__tests__/useSuspenseQueries.types.test.d.ts.map +0 -1
  81. package/build/lib/__tests__/useSuspenseQuery.types.test.d.ts.map +0 -1
  82. package/build/lib/__tests__/utils.d.ts.map +0 -1
  83. package/build/lib/errorBoundaryUtils.d.ts.map +0 -1
  84. package/build/lib/index.d.ts.map +0 -1
  85. package/build/lib/isRestoring.d.ts.map +0 -1
  86. package/build/lib/queryOptions.d.ts.map +0 -1
  87. package/build/lib/reactBatchedUpdates.d.ts.map +0 -1
  88. package/build/lib/reactBatchedUpdates.native.d.ts.map +0 -1
  89. package/build/lib/setBatchUpdatesFn.d.ts.map +0 -1
  90. package/build/lib/suspense.d.ts.map +0 -1
  91. package/build/lib/types.d.ts.map +0 -1
  92. package/build/lib/useBaseQuery.d.ts.map +0 -1
  93. package/build/lib/useInfiniteQuery.d.ts.map +0 -1
  94. package/build/lib/useIsFetching.d.ts.map +0 -1
  95. package/build/lib/useIsMutating.d.ts.map +0 -1
  96. package/build/lib/useMutation.d.ts.map +0 -1
  97. package/build/lib/useQueries.d.ts.map +0 -1
  98. package/build/lib/useQuery.d.ts.map +0 -1
  99. package/build/lib/useSuspenseQueries.d.ts.map +0 -1
  100. package/build/lib/useSuspenseQuery.d.ts.map +0 -1
  101. package/build/lib/useSyncExternalStore.d.ts.map +0 -1
  102. package/build/lib/useSyncExternalStore.native.d.ts.map +0 -1
  103. package/build/lib/utils.d.ts.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-query",
3
- "version": "4.40.1",
3
+ "version": "4.41.0",
4
4
  "description": "Hooks for managing, caching and syncing asynchronous and remote data in React",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
@@ -51,7 +51,7 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "use-sync-external-store": "^1.2.0",
54
- "@tanstack/query-core": "4.40.0"
54
+ "@tanstack/query-core": "4.41.0"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
@@ -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
+ })
@@ -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
+ })
@@ -19,13 +19,14 @@ const queryFn = () => Promise.resolve({ field: 'success' })
19
19
  describe('queryOptions', () => {
20
20
  it('should be used with useQuery', () => {
21
21
  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 }>>()
29
30
  expectTypeOf(
30
31
  useQuery({
31
32
  ...queryOptions({
@@ -1238,3 +1238,233 @@ describe('useQueries with suspense', () => {
1238
1238
  expect(results).toEqual(['1', '2', 'loading'])
1239
1239
  })
1240
1240
  })
1241
+
1242
+ describe('cacheTime minimum enforcement with suspense', () => {
1243
+ const queryClient = createQueryClient()
1244
+
1245
+ it('should not cause infinite re-renders with synchronous query function and cacheTime: 0', async () => {
1246
+ const key = queryKey()
1247
+ let renderCount = 0
1248
+ let queryFnCallCount = 0
1249
+ const maxChecks = 20
1250
+
1251
+ function Page() {
1252
+ renderCount++
1253
+
1254
+ if (renderCount > maxChecks) {
1255
+ throw new Error(`Infinite loop detected! Renders: ${renderCount}`)
1256
+ }
1257
+
1258
+ const result = useQuery(
1259
+ key,
1260
+ () => {
1261
+ queryFnCallCount++
1262
+ return 42
1263
+ },
1264
+ {
1265
+ cacheTime: 0,
1266
+ suspense: true,
1267
+ },
1268
+ )
1269
+
1270
+ return <div>data: {result.data}</div>
1271
+ }
1272
+
1273
+ const rendered = renderWithClient(
1274
+ queryClient,
1275
+ <React.Suspense fallback="loading">
1276
+ <Page />
1277
+ </React.Suspense>,
1278
+ )
1279
+
1280
+ await waitFor(() => rendered.getByText('data: 42'))
1281
+
1282
+ expect(renderCount).toBeLessThan(5)
1283
+ expect(queryFnCallCount).toBe(1)
1284
+ expect(rendered.queryByText('data: 42')).not.toBeNull()
1285
+ expect(rendered.queryByText('loading')).toBeNull()
1286
+ })
1287
+
1288
+ describe('boundary value tests', () => {
1289
+ test.each([
1290
+ [0, 1000],
1291
+ [1, 1000],
1292
+ [999, 1000],
1293
+ [1000, 1000],
1294
+ [2000, 2000],
1295
+ ])(
1296
+ 'cacheTime %i should be adjusted to %i with suspense',
1297
+ async (input, expected) => {
1298
+ const key = queryKey()
1299
+
1300
+ function Page() {
1301
+ const result = useQuery(key, () => 42, {
1302
+ suspense: true,
1303
+ cacheTime: input,
1304
+ })
1305
+ return <div>data: {result.data}</div>
1306
+ }
1307
+
1308
+ const rendered = renderWithClient(
1309
+ queryClient,
1310
+ <React.Suspense fallback="loading">
1311
+ <Page />
1312
+ </React.Suspense>,
1313
+ )
1314
+
1315
+ await waitFor(() => rendered.getByText('data: 42'))
1316
+
1317
+ const query = queryClient.getQueryCache().find(key)
1318
+ const options = query?.options
1319
+ expect(options?.cacheTime).toBe(expected)
1320
+ },
1321
+ )
1322
+ })
1323
+
1324
+ it('should preserve user cacheTime when >= 1000ms', async () => {
1325
+ const key = queryKey()
1326
+ const userCacheTime = 5000
1327
+
1328
+ function Page() {
1329
+ useQuery(key, () => 'test', {
1330
+ suspense: true,
1331
+ cacheTime: userCacheTime,
1332
+ })
1333
+ return <div>rendered</div>
1334
+ }
1335
+
1336
+ renderWithClient(
1337
+ queryClient,
1338
+ <React.Suspense fallback="loading">
1339
+ <Page />
1340
+ </React.Suspense>,
1341
+ )
1342
+
1343
+ await waitFor(() => {
1344
+ const query = queryClient.getQueryCache().find(key)
1345
+ const options = query?.options
1346
+ expect(options?.cacheTime).toBe(userCacheTime)
1347
+ })
1348
+ })
1349
+
1350
+ it('should handle async queries with adjusted cacheTime', async () => {
1351
+ const key = queryKey()
1352
+ let renderCount = 0
1353
+
1354
+ function Page() {
1355
+ renderCount++
1356
+ const result = useQuery(
1357
+ key,
1358
+ async () => {
1359
+ await sleep(10)
1360
+ return 'async-result'
1361
+ },
1362
+ {
1363
+ suspense: true,
1364
+ cacheTime: 0,
1365
+ },
1366
+ )
1367
+ return <div>data: {result.data}</div>
1368
+ }
1369
+
1370
+ const rendered = renderWithClient(
1371
+ queryClient,
1372
+ <React.Suspense fallback="loading">
1373
+ <Page />
1374
+ </React.Suspense>,
1375
+ )
1376
+
1377
+ await waitFor(() => rendered.getByText('data: async-result'))
1378
+ expect(renderCount).toBeLessThan(5)
1379
+ })
1380
+
1381
+ describe('staleTime and cacheTime relationship', () => {
1382
+ it('should handle when both need adjustment', async () => {
1383
+ const key = queryKey()
1384
+
1385
+ function Page() {
1386
+ useQuery(key, () => 42, {
1387
+ suspense: true,
1388
+ cacheTime: 0,
1389
+ staleTime: undefined,
1390
+ })
1391
+ return <div>rendered</div>
1392
+ }
1393
+
1394
+ renderWithClient(
1395
+ queryClient,
1396
+ <React.Suspense fallback="loading">
1397
+ <Page />
1398
+ </React.Suspense>,
1399
+ )
1400
+
1401
+ await waitFor(() => {
1402
+ const query = queryClient.getQueryCache().find(key)
1403
+ const options = query?.options as any
1404
+ expect(options?.cacheTime).toBe(1000)
1405
+ expect(options?.staleTime).toBe(1000)
1406
+ })
1407
+ })
1408
+
1409
+ it('should maintain staleTime < cacheTime invariant', async () => {
1410
+ const key = queryKey()
1411
+
1412
+ function Page() {
1413
+ useQuery(key, () => 42, {
1414
+ suspense: true,
1415
+ cacheTime: 500,
1416
+ staleTime: 2000,
1417
+ })
1418
+ return <div>rendered</div>
1419
+ }
1420
+
1421
+ renderWithClient(
1422
+ queryClient,
1423
+ <React.Suspense fallback="loading">
1424
+ <Page />
1425
+ </React.Suspense>,
1426
+ )
1427
+
1428
+ await waitFor(() => {
1429
+ const query = queryClient.getQueryCache().find(key)
1430
+ const options = query?.options as any
1431
+ expect(options?.cacheTime).toBe(1000)
1432
+ expect(options?.staleTime).toBe(2000)
1433
+ })
1434
+ })
1435
+ })
1436
+
1437
+ it('should fix synchronous query with cacheTime 0 infinite loop', async () => {
1438
+ const key = queryKey()
1439
+ let renderCount = 0
1440
+ let queryFnCallCount = 0
1441
+
1442
+ function Page() {
1443
+ renderCount++
1444
+ const result = useQuery(
1445
+ key,
1446
+ () => {
1447
+ queryFnCallCount++
1448
+ return 42
1449
+ },
1450
+ {
1451
+ suspense: true,
1452
+ cacheTime: 0,
1453
+ },
1454
+ )
1455
+ return <div>data: {result.data}</div>
1456
+ }
1457
+
1458
+ const rendered = renderWithClient(
1459
+ queryClient,
1460
+ <React.Suspense fallback="loading">
1461
+ <Page />
1462
+ </React.Suspense>,
1463
+ )
1464
+
1465
+ await waitFor(() => rendered.getByText('data: 42'))
1466
+
1467
+ expect(renderCount).toBeLessThan(5)
1468
+ expect(queryFnCallCount).toBe(1)
1469
+ })
1470
+ })
@@ -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
+ })
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ export { useQueries } from './useQueries'
12
12
  export type { QueriesResults, QueriesOptions } from './useQueries'
13
13
  export { useQuery } from './useQuery'
14
14
  export { useSuspenseQuery } from './useSuspenseQuery'
15
+ export { useSuspenseInfiniteQuery } from './useSuspenseInfiniteQuery'
15
16
  export { useSuspenseQueries } from './useSuspenseQueries'
16
17
  export type {
17
18
  SuspenseQueriesResults,
@@ -22,6 +23,11 @@ export type {
22
23
  DefinedInitialDataOptions,
23
24
  UndefinedInitialDataOptions,
24
25
  } from './queryOptions'
26
+ export { infiniteQueryOptions } from './infiniteQueryOptions'
27
+ export type {
28
+ DefinedInitialDataInfiniteOptions,
29
+ UndefinedInitialDataInfiniteOptions,
30
+ } from './infiniteQueryOptions'
25
31
  export {
26
32
  defaultContext,
27
33
  QueryClientProvider,
@@ -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
+ }
@@ -59,7 +59,10 @@ export function queryOptions<
59
59
  'queryKey'
60
60
  >,
61
61
  ): WithRequired<
62
- DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
62
+ OmitKeyof<
63
+ DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
64
+ ProhibitedQueryOptionsKeyInV5
65
+ >,
63
66
  'queryKey'
64
67
  >
65
68
 
@@ -77,7 +80,10 @@ export function queryOptions<
77
80
  'queryKey'
78
81
  >,
79
82
  ): WithRequired<
80
- UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
83
+ OmitKeyof<
84
+ UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
85
+ ProhibitedQueryOptionsKeyInV5
86
+ >,
81
87
  'queryKey'
82
88
  >
83
89
 
package/src/suspense.ts CHANGED
@@ -4,6 +4,13 @@ import type { QueryErrorResetBoundaryValue } from './QueryErrorResetBoundary'
4
4
  import type { QueryObserverResult } from '@tanstack/query-core'
5
5
  import type { QueryKey } from '@tanstack/query-core'
6
6
 
7
+ /**
8
+ * Ensures minimum staleTime and cacheTime values when suspense is enabled.
9
+ * Despite the name, this function guards both staleTime and cacheTime to prevent
10
+ * infinite re-render loops with synchronous queries.
11
+ *
12
+ * @deprecated in v5 - replaced by ensureSuspenseTimers
13
+ */
7
14
  export const ensureStaleTime = (
8
15
  defaultedOptions: DefaultedQueryObserverOptions<any, any, any, any, any>,
9
16
  ) => {
@@ -13,6 +20,10 @@ export const ensureStaleTime = (
13
20
  if (typeof defaultedOptions.staleTime !== 'number') {
14
21
  defaultedOptions.staleTime = 1000
15
22
  }
23
+
24
+ if (typeof defaultedOptions.cacheTime === 'number') {
25
+ defaultedOptions.cacheTime = Math.max(defaultedOptions.cacheTime, 1000)
26
+ }
16
27
  }
17
28
  }
18
29