@tanstack/react-query 5.0.0-beta.9 → 5.0.0-rc.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.
- package/build/codemods/coverage/clover.xml +2 -2
- package/build/codemods/coverage/index.html +1 -1
- package/build/legacy/HydrationBoundary.cjs +37 -2
- package/build/legacy/HydrationBoundary.cjs.map +1 -1
- package/build/legacy/HydrationBoundary.d.cts +3 -1
- package/build/legacy/HydrationBoundary.d.ts +3 -1
- package/build/legacy/HydrationBoundary.js +37 -2
- package/build/legacy/HydrationBoundary.js.map +1 -1
- package/build/legacy/index.cjs +3 -0
- package/build/legacy/index.cjs.map +1 -1
- package/build/legacy/index.d.cts +1 -0
- package/build/legacy/index.d.ts +1 -0
- package/build/legacy/index.js +2 -0
- package/build/legacy/index.js.map +1 -1
- package/build/legacy/queryOptions.cjs.map +1 -1
- package/build/legacy/queryOptions.d.cts +7 -3
- package/build/legacy/queryOptions.d.ts +7 -3
- package/build/legacy/queryOptions.js.map +1 -1
- package/build/legacy/suspense.cjs +3 -0
- package/build/legacy/suspense.cjs.map +1 -1
- package/build/legacy/suspense.d.cts +3 -2
- package/build/legacy/suspense.d.ts +3 -2
- package/build/legacy/suspense.js +2 -0
- package/build/legacy/suspense.js.map +1 -1
- package/build/legacy/types.cjs.map +1 -1
- package/build/legacy/types.d.cts +4 -4
- package/build/legacy/types.d.ts +4 -4
- package/build/legacy/useQueries.cjs.map +1 -1
- package/build/legacy/useQueries.d.cts +4 -4
- package/build/legacy/useQueries.d.ts +4 -4
- package/build/legacy/useQueries.js.map +1 -1
- package/build/legacy/useSuspenseQueries.cjs +47 -0
- package/build/legacy/useSuspenseQueries.cjs.map +1 -0
- package/build/legacy/useSuspenseQueries.d.cts +66 -0
- package/build/legacy/useSuspenseQueries.d.ts +66 -0
- package/build/legacy/useSuspenseQueries.js +23 -0
- package/build/legacy/useSuspenseQueries.js.map +1 -0
- package/build/legacy/useSuspenseQuery.cjs +2 -1
- package/build/legacy/useSuspenseQuery.cjs.map +1 -1
- package/build/legacy/useSuspenseQuery.js +2 -1
- package/build/legacy/useSuspenseQuery.js.map +1 -1
- package/build/legacy/utils.cjs.map +1 -1
- package/build/legacy/utils.d.cts +1 -1
- package/build/legacy/utils.d.ts +1 -1
- package/build/legacy/utils.js.map +1 -1
- package/build/modern/HydrationBoundary.cjs +37 -2
- package/build/modern/HydrationBoundary.cjs.map +1 -1
- package/build/modern/HydrationBoundary.d.cts +3 -1
- package/build/modern/HydrationBoundary.d.ts +3 -1
- package/build/modern/HydrationBoundary.js +37 -2
- package/build/modern/HydrationBoundary.js.map +1 -1
- package/build/modern/index.cjs +3 -0
- package/build/modern/index.cjs.map +1 -1
- package/build/modern/index.d.cts +1 -0
- package/build/modern/index.d.ts +1 -0
- package/build/modern/index.js +2 -0
- package/build/modern/index.js.map +1 -1
- package/build/modern/queryOptions.cjs.map +1 -1
- package/build/modern/queryOptions.d.cts +7 -3
- package/build/modern/queryOptions.d.ts +7 -3
- package/build/modern/queryOptions.js.map +1 -1
- package/build/modern/suspense.cjs +3 -0
- package/build/modern/suspense.cjs.map +1 -1
- package/build/modern/suspense.d.cts +3 -2
- package/build/modern/suspense.d.ts +3 -2
- package/build/modern/suspense.js +2 -0
- package/build/modern/suspense.js.map +1 -1
- package/build/modern/types.cjs.map +1 -1
- package/build/modern/types.d.cts +4 -4
- package/build/modern/types.d.ts +4 -4
- package/build/modern/useQueries.cjs.map +1 -1
- package/build/modern/useQueries.d.cts +4 -4
- package/build/modern/useQueries.d.ts +4 -4
- package/build/modern/useQueries.js.map +1 -1
- package/build/modern/useSuspenseQueries.cjs +47 -0
- package/build/modern/useSuspenseQueries.cjs.map +1 -0
- package/build/modern/useSuspenseQueries.d.cts +66 -0
- package/build/modern/useSuspenseQueries.d.ts +66 -0
- package/build/modern/useSuspenseQueries.js +23 -0
- package/build/modern/useSuspenseQueries.js.map +1 -0
- package/build/modern/useSuspenseQuery.cjs +2 -1
- package/build/modern/useSuspenseQuery.cjs.map +1 -1
- package/build/modern/useSuspenseQuery.js +2 -1
- package/build/modern/useSuspenseQuery.js.map +1 -1
- package/build/modern/utils.cjs.map +1 -1
- package/build/modern/utils.d.cts +1 -1
- package/build/modern/utils.d.ts +1 -1
- package/build/modern/utils.js.map +1 -1
- package/package.json +7 -3
- package/src/HydrationBoundary.tsx +78 -8
- package/src/__tests__/HydrationBoundary.test.tsx +111 -7
- package/src/__tests__/QueryResetErrorBoundary.test.tsx +10 -6
- package/src/__tests__/fine-grained-persister.test.tsx +163 -0
- package/src/__tests__/queryOptions.types.test.tsx +31 -0
- package/src/__tests__/ssr.test.tsx +2 -2
- package/src/__tests__/suspense.test.tsx +94 -386
- package/src/__tests__/useInfiniteQuery.test.tsx +44 -44
- package/src/__tests__/useInfiniteQuery.type.test.tsx +10 -10
- package/src/__tests__/useIsFetching.test.tsx +2 -2
- package/src/__tests__/useMutation.test.tsx +4 -4
- package/src/__tests__/useMutationState.test.tsx +4 -4
- package/src/__tests__/useQueries.test.tsx +65 -64
- package/src/__tests__/useQuery.test.tsx +142 -141
- package/src/__tests__/useQuery.types.test.tsx +34 -0
- package/src/__tests__/utils.tsx +0 -7
- package/src/index.ts +5 -0
- package/src/queryOptions.ts +11 -3
- package/src/suspense.ts +12 -0
- package/src/types.ts +18 -12
- package/src/useQueries.ts +34 -28
- package/src/useSuspenseQueries.ts +164 -0
- package/src/useSuspenseQuery.ts +2 -1
- package/src/utils.ts +1 -1
|
@@ -5,21 +5,25 @@ import { vi } from 'vitest'
|
|
|
5
5
|
import {
|
|
6
6
|
QueryCache,
|
|
7
7
|
QueryErrorResetBoundary,
|
|
8
|
-
useInfiniteQuery,
|
|
9
|
-
useQueries,
|
|
10
|
-
useQuery,
|
|
11
8
|
useQueryErrorResetBoundary,
|
|
9
|
+
useSuspenseInfiniteQuery,
|
|
10
|
+
useSuspenseQueries,
|
|
11
|
+
useSuspenseQuery,
|
|
12
12
|
} from '..'
|
|
13
13
|
import { createQueryClient, queryKey, renderWithClient, sleep } from './utils'
|
|
14
|
-
import type {
|
|
14
|
+
import type {
|
|
15
|
+
InfiniteData,
|
|
16
|
+
UseSuspenseInfiniteQueryResult,
|
|
17
|
+
UseSuspenseQueryResult,
|
|
18
|
+
} from '..'
|
|
15
19
|
|
|
16
|
-
describe(
|
|
20
|
+
describe('useSuspenseQuery', () => {
|
|
17
21
|
const queryCache = new QueryCache()
|
|
18
22
|
const queryClient = createQueryClient({ queryCache })
|
|
19
23
|
|
|
20
24
|
it('should render the correct amount of times in Suspense mode', async () => {
|
|
21
25
|
const key = queryKey()
|
|
22
|
-
const states:
|
|
26
|
+
const states: Array<UseSuspenseQueryResult<number>> = []
|
|
23
27
|
|
|
24
28
|
let count = 0
|
|
25
29
|
let renders = 0
|
|
@@ -29,14 +33,13 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
29
33
|
|
|
30
34
|
const [stateKey, setStateKey] = React.useState(key)
|
|
31
35
|
|
|
32
|
-
const state =
|
|
36
|
+
const state = useSuspenseQuery({
|
|
33
37
|
queryKey: stateKey,
|
|
34
38
|
queryFn: async () => {
|
|
35
39
|
count++
|
|
36
40
|
await sleep(10)
|
|
37
41
|
return count
|
|
38
42
|
},
|
|
39
|
-
suspense: true,
|
|
40
43
|
})
|
|
41
44
|
|
|
42
45
|
states.push(state)
|
|
@@ -69,18 +72,18 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
69
72
|
|
|
70
73
|
it('should return the correct states for a successful infinite query', async () => {
|
|
71
74
|
const key = queryKey()
|
|
72
|
-
const states:
|
|
75
|
+
const states: Array<UseSuspenseInfiniteQueryResult<InfiniteData<number>>> =
|
|
76
|
+
[]
|
|
73
77
|
|
|
74
78
|
function Page() {
|
|
75
79
|
const [multiplier, setMultiplier] = React.useState(1)
|
|
76
|
-
const state =
|
|
80
|
+
const state = useSuspenseInfiniteQuery({
|
|
77
81
|
queryKey: [`${key}_${multiplier}`],
|
|
78
82
|
queryFn: async ({ pageParam }) => {
|
|
79
83
|
await sleep(10)
|
|
80
84
|
return Number(pageParam * multiplier)
|
|
81
85
|
},
|
|
82
|
-
|
|
83
|
-
defaultPageParam: 1,
|
|
86
|
+
initialPageParam: 1,
|
|
84
87
|
getNextPageParam: (lastPage) => lastPage + 1,
|
|
85
88
|
})
|
|
86
89
|
states.push(state)
|
|
@@ -120,14 +123,14 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
120
123
|
it('should not call the queryFn twice when used in Suspense mode', async () => {
|
|
121
124
|
const key = queryKey()
|
|
122
125
|
|
|
123
|
-
const queryFn = vi.fn<unknown
|
|
126
|
+
const queryFn = vi.fn<Array<unknown>, string>()
|
|
124
127
|
queryFn.mockImplementation(() => {
|
|
125
128
|
sleep(10)
|
|
126
129
|
return 'data'
|
|
127
130
|
})
|
|
128
131
|
|
|
129
132
|
function Page() {
|
|
130
|
-
|
|
133
|
+
useSuspenseQuery({ queryKey: [key], queryFn })
|
|
131
134
|
|
|
132
135
|
return <>rendered</>
|
|
133
136
|
}
|
|
@@ -148,13 +151,12 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
148
151
|
const key = queryKey()
|
|
149
152
|
|
|
150
153
|
function Page() {
|
|
151
|
-
|
|
154
|
+
useSuspenseQuery({
|
|
152
155
|
queryKey: key,
|
|
153
156
|
queryFn: () => {
|
|
154
157
|
sleep(10)
|
|
155
158
|
return 'data'
|
|
156
159
|
},
|
|
157
|
-
suspense: true,
|
|
158
160
|
})
|
|
159
161
|
|
|
160
162
|
return <>rendered</>
|
|
@@ -200,7 +202,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
200
202
|
let succeed = false
|
|
201
203
|
|
|
202
204
|
function Page() {
|
|
203
|
-
|
|
205
|
+
useSuspenseQuery({
|
|
204
206
|
queryKey: key,
|
|
205
207
|
queryFn: async () => {
|
|
206
208
|
await sleep(10)
|
|
@@ -212,7 +214,6 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
212
214
|
}
|
|
213
215
|
},
|
|
214
216
|
retryDelay: 10,
|
|
215
|
-
suspense: true,
|
|
216
217
|
})
|
|
217
218
|
|
|
218
219
|
return <div>rendered</div>
|
|
@@ -272,7 +273,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
272
273
|
let succeed = false
|
|
273
274
|
|
|
274
275
|
function Page() {
|
|
275
|
-
|
|
276
|
+
useSuspenseQuery({
|
|
276
277
|
queryKey: key,
|
|
277
278
|
queryFn: async () => {
|
|
278
279
|
await sleep(10)
|
|
@@ -283,7 +284,6 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
283
284
|
}
|
|
284
285
|
},
|
|
285
286
|
retry: false,
|
|
286
|
-
suspense: true,
|
|
287
287
|
})
|
|
288
288
|
return <div>rendered</div>
|
|
289
289
|
}
|
|
@@ -332,7 +332,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
332
332
|
let count = 0
|
|
333
333
|
|
|
334
334
|
function Component() {
|
|
335
|
-
const result =
|
|
335
|
+
const result = useSuspenseQuery({
|
|
336
336
|
queryKey: key,
|
|
337
337
|
queryFn: async () => {
|
|
338
338
|
await sleep(100)
|
|
@@ -340,7 +340,6 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
340
340
|
return count
|
|
341
341
|
},
|
|
342
342
|
retry: false,
|
|
343
|
-
suspense: true,
|
|
344
343
|
staleTime: 0,
|
|
345
344
|
})
|
|
346
345
|
return (
|
|
@@ -388,7 +387,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
388
387
|
const key2 = queryKey()
|
|
389
388
|
|
|
390
389
|
function Component(props: { queryKey: Array<string> }) {
|
|
391
|
-
const result =
|
|
390
|
+
const result = useSuspenseQuery({
|
|
392
391
|
queryKey: props.queryKey,
|
|
393
392
|
queryFn: async () => {
|
|
394
393
|
await sleep(100)
|
|
@@ -396,7 +395,6 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
396
395
|
},
|
|
397
396
|
|
|
398
397
|
retry: false,
|
|
399
|
-
suspense: true,
|
|
400
398
|
})
|
|
401
399
|
return <div>data: {result.data}</div>
|
|
402
400
|
}
|
|
@@ -437,7 +435,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
437
435
|
let succeed = false
|
|
438
436
|
|
|
439
437
|
function Page() {
|
|
440
|
-
|
|
438
|
+
useSuspenseQuery({
|
|
441
439
|
queryKey: key,
|
|
442
440
|
queryFn: async () => {
|
|
443
441
|
await sleep(10)
|
|
@@ -448,7 +446,6 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
448
446
|
}
|
|
449
447
|
},
|
|
450
448
|
retry: false,
|
|
451
|
-
suspense: true,
|
|
452
449
|
})
|
|
453
450
|
return <div>rendered</div>
|
|
454
451
|
}
|
|
@@ -499,14 +496,13 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
499
496
|
const key = queryKey()
|
|
500
497
|
|
|
501
498
|
function Page() {
|
|
502
|
-
|
|
499
|
+
useSuspenseQuery({
|
|
503
500
|
queryKey: key,
|
|
504
501
|
queryFn: async (): Promise<unknown> => {
|
|
505
502
|
await sleep(10)
|
|
506
503
|
throw new Error('Suspense Error a1x')
|
|
507
504
|
},
|
|
508
505
|
retry: false,
|
|
509
|
-
suspense: true,
|
|
510
506
|
})
|
|
511
507
|
return <div>rendered</div>
|
|
512
508
|
}
|
|
@@ -534,171 +530,6 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
534
530
|
consoleMock.mockRestore()
|
|
535
531
|
})
|
|
536
532
|
|
|
537
|
-
it('should not throw errors to the error boundary when throwOnError: false', async () => {
|
|
538
|
-
const key = queryKey()
|
|
539
|
-
|
|
540
|
-
function Page() {
|
|
541
|
-
useQuery({
|
|
542
|
-
queryKey: key,
|
|
543
|
-
queryFn: async (): Promise<unknown> => {
|
|
544
|
-
await sleep(10)
|
|
545
|
-
throw new Error('Suspense Error a2x')
|
|
546
|
-
},
|
|
547
|
-
retry: false,
|
|
548
|
-
suspense: true,
|
|
549
|
-
throwOnError: false,
|
|
550
|
-
})
|
|
551
|
-
return <div>rendered</div>
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
function App() {
|
|
555
|
-
return (
|
|
556
|
-
<ErrorBoundary
|
|
557
|
-
fallbackRender={() => (
|
|
558
|
-
<div>
|
|
559
|
-
<div>error boundary</div>
|
|
560
|
-
</div>
|
|
561
|
-
)}
|
|
562
|
-
>
|
|
563
|
-
<React.Suspense fallback="Loading...">
|
|
564
|
-
<Page />
|
|
565
|
-
</React.Suspense>
|
|
566
|
-
</ErrorBoundary>
|
|
567
|
-
)
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
const rendered = renderWithClient(queryClient, <App />)
|
|
571
|
-
|
|
572
|
-
await waitFor(() => rendered.getByText('Loading...'))
|
|
573
|
-
await waitFor(() => rendered.getByText('rendered'))
|
|
574
|
-
})
|
|
575
|
-
|
|
576
|
-
it('should throw errors to the error boundary when a throwOnError function returns true', async () => {
|
|
577
|
-
const consoleMock = vi
|
|
578
|
-
.spyOn(console, 'error')
|
|
579
|
-
.mockImplementation(() => undefined)
|
|
580
|
-
const key = queryKey()
|
|
581
|
-
|
|
582
|
-
function Page() {
|
|
583
|
-
useQuery({
|
|
584
|
-
queryKey: key,
|
|
585
|
-
queryFn: async (): Promise<unknown> => {
|
|
586
|
-
await sleep(10)
|
|
587
|
-
return Promise.reject(new Error('Remote Error'))
|
|
588
|
-
},
|
|
589
|
-
retry: false,
|
|
590
|
-
suspense: true,
|
|
591
|
-
throwOnError: (err) => err.message !== 'Local Error',
|
|
592
|
-
})
|
|
593
|
-
return <div>rendered</div>
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
function App() {
|
|
597
|
-
return (
|
|
598
|
-
<ErrorBoundary
|
|
599
|
-
fallbackRender={() => (
|
|
600
|
-
<div>
|
|
601
|
-
<div>error boundary</div>
|
|
602
|
-
</div>
|
|
603
|
-
)}
|
|
604
|
-
>
|
|
605
|
-
<React.Suspense fallback="Loading...">
|
|
606
|
-
<Page />
|
|
607
|
-
</React.Suspense>
|
|
608
|
-
</ErrorBoundary>
|
|
609
|
-
)
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
const rendered = renderWithClient(queryClient, <App />)
|
|
613
|
-
|
|
614
|
-
await waitFor(() => rendered.getByText('Loading...'))
|
|
615
|
-
await waitFor(() => rendered.getByText('error boundary'))
|
|
616
|
-
consoleMock.mockRestore()
|
|
617
|
-
})
|
|
618
|
-
|
|
619
|
-
it('should not throw errors to the error boundary when a throwOnError function returns false', async () => {
|
|
620
|
-
const key = queryKey()
|
|
621
|
-
|
|
622
|
-
function Page() {
|
|
623
|
-
useQuery({
|
|
624
|
-
queryKey: key,
|
|
625
|
-
queryFn: async (): Promise<unknown> => {
|
|
626
|
-
await sleep(10)
|
|
627
|
-
return Promise.reject(new Error('Local Error'))
|
|
628
|
-
},
|
|
629
|
-
retry: false,
|
|
630
|
-
suspense: true,
|
|
631
|
-
throwOnError: (err) => err.message !== 'Local Error',
|
|
632
|
-
})
|
|
633
|
-
return <div>rendered</div>
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
function App() {
|
|
637
|
-
return (
|
|
638
|
-
<ErrorBoundary
|
|
639
|
-
fallbackRender={() => (
|
|
640
|
-
<div>
|
|
641
|
-
<div>error boundary</div>
|
|
642
|
-
</div>
|
|
643
|
-
)}
|
|
644
|
-
>
|
|
645
|
-
<React.Suspense fallback="Loading...">
|
|
646
|
-
<Page />
|
|
647
|
-
</React.Suspense>
|
|
648
|
-
</ErrorBoundary>
|
|
649
|
-
)
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
const rendered = renderWithClient(queryClient, <App />)
|
|
653
|
-
|
|
654
|
-
await waitFor(() => rendered.getByText('Loading...'))
|
|
655
|
-
await waitFor(() => rendered.getByText('rendered'))
|
|
656
|
-
})
|
|
657
|
-
|
|
658
|
-
it('should not call the queryFn when not enabled', async () => {
|
|
659
|
-
const key = queryKey()
|
|
660
|
-
|
|
661
|
-
const queryFn = vi.fn<unknown[], Promise<string>>()
|
|
662
|
-
queryFn.mockImplementation(async () => {
|
|
663
|
-
await sleep(10)
|
|
664
|
-
return '23'
|
|
665
|
-
})
|
|
666
|
-
|
|
667
|
-
function Page() {
|
|
668
|
-
const [enabled, setEnabled] = React.useState(false)
|
|
669
|
-
const result = useQuery({
|
|
670
|
-
queryKey: [key],
|
|
671
|
-
queryFn,
|
|
672
|
-
suspense: true,
|
|
673
|
-
enabled,
|
|
674
|
-
})
|
|
675
|
-
|
|
676
|
-
return (
|
|
677
|
-
<div>
|
|
678
|
-
<button onClick={() => setEnabled(true)}>fire</button>
|
|
679
|
-
<h1>{result.data}</h1>
|
|
680
|
-
</div>
|
|
681
|
-
)
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
const rendered = renderWithClient(
|
|
685
|
-
queryClient,
|
|
686
|
-
<React.Suspense fallback="loading">
|
|
687
|
-
<Page />
|
|
688
|
-
</React.Suspense>,
|
|
689
|
-
)
|
|
690
|
-
|
|
691
|
-
expect(queryFn).toHaveBeenCalledTimes(0)
|
|
692
|
-
|
|
693
|
-
fireEvent.click(rendered.getByRole('button', { name: /fire/i }))
|
|
694
|
-
|
|
695
|
-
await waitFor(() => {
|
|
696
|
-
expect(rendered.getByRole('heading').textContent).toBe('23')
|
|
697
|
-
})
|
|
698
|
-
|
|
699
|
-
expect(queryFn).toHaveBeenCalledTimes(1)
|
|
700
|
-
})
|
|
701
|
-
|
|
702
533
|
it('should error catched in error boundary without infinite loop', async () => {
|
|
703
534
|
const consoleMock = vi
|
|
704
535
|
.spyOn(console, 'error')
|
|
@@ -710,7 +541,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
710
541
|
function Page() {
|
|
711
542
|
const [nonce] = React.useState(0)
|
|
712
543
|
const queryKeys = [`${key}-${succeed}`]
|
|
713
|
-
const result =
|
|
544
|
+
const result = useSuspenseQuery({
|
|
714
545
|
queryKey: queryKeys,
|
|
715
546
|
queryFn: async () => {
|
|
716
547
|
await sleep(10)
|
|
@@ -721,7 +552,6 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
721
552
|
}
|
|
722
553
|
},
|
|
723
554
|
retry: false,
|
|
724
|
-
suspense: true,
|
|
725
555
|
})
|
|
726
556
|
return (
|
|
727
557
|
<div>
|
|
@@ -782,7 +612,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
782
612
|
const [key, rerender] = React.useReducer((x) => x + 1, 0)
|
|
783
613
|
const queryKeys = [key, succeed]
|
|
784
614
|
|
|
785
|
-
const result =
|
|
615
|
+
const result = useSuspenseQuery({
|
|
786
616
|
queryKey: queryKeys,
|
|
787
617
|
queryFn: async () => {
|
|
788
618
|
await sleep(10)
|
|
@@ -793,8 +623,12 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
793
623
|
}
|
|
794
624
|
},
|
|
795
625
|
retry: false,
|
|
796
|
-
suspense: true,
|
|
797
626
|
})
|
|
627
|
+
|
|
628
|
+
if (result.error) {
|
|
629
|
+
throw result.error
|
|
630
|
+
}
|
|
631
|
+
|
|
798
632
|
return (
|
|
799
633
|
<div>
|
|
800
634
|
<span>rendered</span> <span>{result.data}</span>
|
|
@@ -839,77 +673,9 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
839
673
|
consoleMock.mockRestore()
|
|
840
674
|
})
|
|
841
675
|
|
|
842
|
-
it('should error catched in error boundary without infinite loop when enabled changed', async () => {
|
|
843
|
-
const consoleMock = vi
|
|
844
|
-
.spyOn(console, 'error')
|
|
845
|
-
.mockImplementation(() => undefined)
|
|
846
|
-
function Page() {
|
|
847
|
-
const queryKeys = '1'
|
|
848
|
-
const [enabled, setEnabled] = React.useState(false)
|
|
849
|
-
|
|
850
|
-
const result = useQuery<string>({
|
|
851
|
-
queryKey: [queryKeys],
|
|
852
|
-
queryFn: async () => {
|
|
853
|
-
await sleep(10)
|
|
854
|
-
throw new Error('Suspense Error Bingo')
|
|
855
|
-
},
|
|
856
|
-
|
|
857
|
-
retry: false,
|
|
858
|
-
suspense: true,
|
|
859
|
-
enabled,
|
|
860
|
-
})
|
|
861
|
-
return (
|
|
862
|
-
<div>
|
|
863
|
-
<span>rendered</span> <span>{result.data}</span>
|
|
864
|
-
<button
|
|
865
|
-
aria-label="fail"
|
|
866
|
-
onClick={() => {
|
|
867
|
-
setEnabled(true)
|
|
868
|
-
}}
|
|
869
|
-
>
|
|
870
|
-
fail
|
|
871
|
-
</button>
|
|
872
|
-
</div>
|
|
873
|
-
)
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
function App() {
|
|
877
|
-
const { reset } = useQueryErrorResetBoundary()
|
|
878
|
-
return (
|
|
879
|
-
<ErrorBoundary
|
|
880
|
-
onReset={reset}
|
|
881
|
-
fallbackRender={() => <div>error boundary</div>}
|
|
882
|
-
>
|
|
883
|
-
<React.Suspense fallback="Loading...">
|
|
884
|
-
<Page />
|
|
885
|
-
</React.Suspense>
|
|
886
|
-
</ErrorBoundary>
|
|
887
|
-
)
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
const rendered = renderWithClient(queryClient, <App />)
|
|
891
|
-
|
|
892
|
-
// render empty data with 'rendered' when enabled is false
|
|
893
|
-
await waitFor(() => rendered.getByText('rendered'))
|
|
894
|
-
|
|
895
|
-
// change enabled to true
|
|
896
|
-
fireEvent.click(rendered.getByLabelText('fail'))
|
|
897
|
-
|
|
898
|
-
// render pending fallback
|
|
899
|
-
await waitFor(() => rendered.getByText('Loading...'))
|
|
900
|
-
|
|
901
|
-
// render error boundary fallback (error boundary)
|
|
902
|
-
await waitFor(() => rendered.getByText('error boundary'))
|
|
903
|
-
expect(consoleMock).toHaveBeenCalledWith(
|
|
904
|
-
expect.objectContaining(new Error('Suspense Error Bingo')),
|
|
905
|
-
)
|
|
906
|
-
|
|
907
|
-
consoleMock.mockRestore()
|
|
908
|
-
})
|
|
909
|
-
|
|
910
676
|
it('should render the correct amount of times in Suspense mode when gcTime is set to 0', async () => {
|
|
911
677
|
const key = queryKey()
|
|
912
|
-
let state:
|
|
678
|
+
let state: UseSuspenseQueryResult<number> | null = null
|
|
913
679
|
|
|
914
680
|
let count = 0
|
|
915
681
|
let renders = 0
|
|
@@ -917,14 +683,13 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
917
683
|
function Page() {
|
|
918
684
|
renders++
|
|
919
685
|
|
|
920
|
-
state =
|
|
686
|
+
state = useSuspenseQuery({
|
|
921
687
|
queryKey: key,
|
|
922
688
|
queryFn: async () => {
|
|
923
689
|
count++
|
|
924
690
|
await sleep(10)
|
|
925
691
|
return count
|
|
926
692
|
},
|
|
927
|
-
suspense: true,
|
|
928
693
|
gcTime: 0,
|
|
929
694
|
})
|
|
930
695
|
|
|
@@ -952,67 +717,76 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
952
717
|
expect(renders).toBe(2)
|
|
953
718
|
expect(rendered.queryByText('rendered')).not.toBeNull()
|
|
954
719
|
})
|
|
955
|
-
})
|
|
956
720
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
const
|
|
963
|
-
|
|
964
|
-
function Fallback() {
|
|
965
|
-
results.push('loading')
|
|
966
|
-
return <div>loading</div>
|
|
967
|
-
}
|
|
721
|
+
it('should not throw background errors to the error boundary', async () => {
|
|
722
|
+
const consoleMock = vi
|
|
723
|
+
.spyOn(console, 'error')
|
|
724
|
+
.mockImplementation(() => undefined)
|
|
725
|
+
let succeed = true
|
|
726
|
+
const key = queryKey()
|
|
968
727
|
|
|
969
728
|
function Page() {
|
|
970
|
-
const result =
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
{
|
|
982
|
-
queryKey: key2,
|
|
983
|
-
queryFn: async () => {
|
|
984
|
-
results.push('2')
|
|
985
|
-
await sleep(20)
|
|
986
|
-
return '2'
|
|
987
|
-
},
|
|
988
|
-
suspense: true,
|
|
989
|
-
},
|
|
990
|
-
],
|
|
729
|
+
const result = useSuspenseQuery({
|
|
730
|
+
queryKey: key,
|
|
731
|
+
queryFn: async () => {
|
|
732
|
+
await sleep(10)
|
|
733
|
+
if (!succeed) {
|
|
734
|
+
throw new Error('Suspense Error Bingo')
|
|
735
|
+
} else {
|
|
736
|
+
return 'data'
|
|
737
|
+
}
|
|
738
|
+
},
|
|
739
|
+
retry: false,
|
|
991
740
|
})
|
|
741
|
+
|
|
992
742
|
return (
|
|
993
743
|
<div>
|
|
994
|
-
<
|
|
744
|
+
<span>
|
|
745
|
+
rendered {result.data} {result.status}
|
|
746
|
+
</span>
|
|
747
|
+
<button onClick={() => result.refetch()}>refetch</button>
|
|
995
748
|
</div>
|
|
996
749
|
)
|
|
997
750
|
}
|
|
998
751
|
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
<
|
|
1003
|
-
|
|
1004
|
-
|
|
752
|
+
function App() {
|
|
753
|
+
const { reset } = useQueryErrorResetBoundary()
|
|
754
|
+
return (
|
|
755
|
+
<ErrorBoundary
|
|
756
|
+
onReset={reset}
|
|
757
|
+
fallbackRender={() => <div>error boundary</div>}
|
|
758
|
+
>
|
|
759
|
+
<React.Suspense fallback="Loading...">
|
|
760
|
+
<Page />
|
|
761
|
+
</React.Suspense>
|
|
762
|
+
</ErrorBoundary>
|
|
763
|
+
)
|
|
764
|
+
}
|
|
1005
765
|
|
|
1006
|
-
|
|
1007
|
-
await waitFor(() => rendered.getByText('data: 1,2'))
|
|
766
|
+
const rendered = renderWithClient(queryClient, <App />)
|
|
1008
767
|
|
|
1009
|
-
|
|
768
|
+
// render suspense fallback (Loading...)
|
|
769
|
+
await waitFor(() => rendered.getByText('Loading...'))
|
|
770
|
+
// resolve promise -> render Page (rendered)
|
|
771
|
+
await waitFor(() => rendered.getByText('rendered data success'))
|
|
772
|
+
|
|
773
|
+
// change promise result to error
|
|
774
|
+
succeed = false
|
|
775
|
+
// refetch
|
|
776
|
+
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
777
|
+
// we are now in error state but still have data to show
|
|
778
|
+
await waitFor(() => rendered.getByText('rendered data error'))
|
|
779
|
+
|
|
780
|
+
consoleMock.mockRestore()
|
|
1010
781
|
})
|
|
782
|
+
})
|
|
1011
783
|
|
|
1012
|
-
|
|
784
|
+
describe('useSuspenseQueries', () => {
|
|
785
|
+
const queryClient = createQueryClient()
|
|
786
|
+
it('should suspend all queries in parallel', async () => {
|
|
1013
787
|
const key1 = queryKey()
|
|
1014
788
|
const key2 = queryKey()
|
|
1015
|
-
const results: string
|
|
789
|
+
const results: Array<string> = []
|
|
1016
790
|
|
|
1017
791
|
function Fallback() {
|
|
1018
792
|
results.push('loading')
|
|
@@ -1020,34 +794,29 @@ describe('useQueries with suspense', () => {
|
|
|
1020
794
|
}
|
|
1021
795
|
|
|
1022
796
|
function Page() {
|
|
1023
|
-
const result =
|
|
797
|
+
const result = useSuspenseQueries({
|
|
1024
798
|
queries: [
|
|
1025
799
|
{
|
|
1026
800
|
queryKey: key1,
|
|
1027
801
|
queryFn: async () => {
|
|
1028
802
|
results.push('1')
|
|
1029
|
-
await sleep(
|
|
803
|
+
await sleep(10)
|
|
1030
804
|
return '1'
|
|
1031
805
|
},
|
|
1032
|
-
suspense: true,
|
|
1033
806
|
},
|
|
1034
807
|
{
|
|
1035
808
|
queryKey: key2,
|
|
1036
809
|
queryFn: async () => {
|
|
1037
810
|
results.push('2')
|
|
1038
|
-
await sleep(
|
|
811
|
+
await sleep(20)
|
|
1039
812
|
return '2'
|
|
1040
813
|
},
|
|
1041
|
-
staleTime: 2000,
|
|
1042
|
-
suspense: false,
|
|
1043
814
|
},
|
|
1044
815
|
],
|
|
1045
816
|
})
|
|
1046
|
-
|
|
1047
817
|
return (
|
|
1048
818
|
<div>
|
|
1049
819
|
<h1>data: {result.map((it) => it.data ?? 'null').join(',')}</h1>
|
|
1050
|
-
<h2>status: {result.map((it) => it.status).join(',')}</h2>
|
|
1051
820
|
</div>
|
|
1052
821
|
)
|
|
1053
822
|
}
|
|
@@ -1058,9 +827,8 @@ describe('useQueries with suspense', () => {
|
|
|
1058
827
|
<Page />
|
|
1059
828
|
</React.Suspense>,
|
|
1060
829
|
)
|
|
830
|
+
|
|
1061
831
|
await waitFor(() => rendered.getByText('loading'))
|
|
1062
|
-
await waitFor(() => rendered.getByText('status: success,pending'))
|
|
1063
|
-
await waitFor(() => rendered.getByText('data: 1,null'))
|
|
1064
832
|
await waitFor(() => rendered.getByText('data: 1,2'))
|
|
1065
833
|
|
|
1066
834
|
expect(results).toEqual(['1', '2', 'loading'])
|
|
@@ -1069,8 +837,8 @@ describe('useQueries with suspense', () => {
|
|
|
1069
837
|
it("shouldn't unmount before all promises fetched", async () => {
|
|
1070
838
|
const key1 = queryKey()
|
|
1071
839
|
const key2 = queryKey()
|
|
1072
|
-
const results: string
|
|
1073
|
-
const refs: number
|
|
840
|
+
const results: Array<string> = []
|
|
841
|
+
const refs: Array<number> = []
|
|
1074
842
|
|
|
1075
843
|
function Fallback() {
|
|
1076
844
|
results.push('loading')
|
|
@@ -1079,7 +847,7 @@ describe('useQueries with suspense', () => {
|
|
|
1079
847
|
|
|
1080
848
|
function Page() {
|
|
1081
849
|
const ref = React.useRef(Math.random())
|
|
1082
|
-
const result =
|
|
850
|
+
const result = useSuspenseQueries({
|
|
1083
851
|
queries: [
|
|
1084
852
|
{
|
|
1085
853
|
queryKey: key1,
|
|
@@ -1089,7 +857,6 @@ describe('useQueries with suspense', () => {
|
|
|
1089
857
|
await sleep(10)
|
|
1090
858
|
return '1'
|
|
1091
859
|
},
|
|
1092
|
-
suspense: true,
|
|
1093
860
|
},
|
|
1094
861
|
{
|
|
1095
862
|
queryKey: key2,
|
|
@@ -1099,7 +866,6 @@ describe('useQueries with suspense', () => {
|
|
|
1099
866
|
await sleep(20)
|
|
1100
867
|
return '2'
|
|
1101
868
|
},
|
|
1102
|
-
suspense: true,
|
|
1103
869
|
},
|
|
1104
870
|
],
|
|
1105
871
|
})
|
|
@@ -1121,62 +887,4 @@ describe('useQueries with suspense', () => {
|
|
|
1121
887
|
await waitFor(() => rendered.getByText('data: 1,2'))
|
|
1122
888
|
expect(refs[0]).toBe(refs[1])
|
|
1123
889
|
})
|
|
1124
|
-
|
|
1125
|
-
it('should suspend all queries in parallel - global configuration', async () => {
|
|
1126
|
-
const queryClientSuspenseMode = createQueryClient({
|
|
1127
|
-
defaultOptions: {
|
|
1128
|
-
queries: {
|
|
1129
|
-
suspense: true,
|
|
1130
|
-
},
|
|
1131
|
-
},
|
|
1132
|
-
})
|
|
1133
|
-
const key1 = queryKey()
|
|
1134
|
-
const key2 = queryKey()
|
|
1135
|
-
const results: string[] = []
|
|
1136
|
-
|
|
1137
|
-
function Fallback() {
|
|
1138
|
-
results.push('loading')
|
|
1139
|
-
return <div>loading</div>
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
function Page() {
|
|
1143
|
-
const result = useQueries({
|
|
1144
|
-
queries: [
|
|
1145
|
-
{
|
|
1146
|
-
queryKey: key1,
|
|
1147
|
-
queryFn: async () => {
|
|
1148
|
-
results.push('1')
|
|
1149
|
-
await sleep(10)
|
|
1150
|
-
return '1'
|
|
1151
|
-
},
|
|
1152
|
-
},
|
|
1153
|
-
{
|
|
1154
|
-
queryKey: key2,
|
|
1155
|
-
queryFn: async () => {
|
|
1156
|
-
results.push('2')
|
|
1157
|
-
await sleep(20)
|
|
1158
|
-
return '2'
|
|
1159
|
-
},
|
|
1160
|
-
},
|
|
1161
|
-
],
|
|
1162
|
-
})
|
|
1163
|
-
return (
|
|
1164
|
-
<div>
|
|
1165
|
-
<h1>data: {result.map((it) => it.data ?? 'null').join(',')}</h1>
|
|
1166
|
-
</div>
|
|
1167
|
-
)
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
const rendered = renderWithClient(
|
|
1171
|
-
queryClientSuspenseMode,
|
|
1172
|
-
<React.Suspense fallback={<Fallback />}>
|
|
1173
|
-
<Page />
|
|
1174
|
-
</React.Suspense>,
|
|
1175
|
-
)
|
|
1176
|
-
|
|
1177
|
-
await waitFor(() => rendered.getByText('loading'))
|
|
1178
|
-
await waitFor(() => rendered.getByText('data: 1,2'))
|
|
1179
|
-
|
|
1180
|
-
expect(results).toEqual(['1', '2', 'loading'])
|
|
1181
|
-
})
|
|
1182
890
|
})
|