8
8
QueryClient ,
9
9
QueryClientProvider ,
10
10
dehydrate ,
11
- hydrate ,
12
11
useQuery ,
13
12
} from '..'
13
+ import type { hydrate } from '@tanstack/query-core'
14
14
15
15
describe ( 'React hydration' , ( ) => {
16
16
let stringifiedState : string
@@ -368,6 +368,7 @@ describe('React hydration', () => {
368
368
369
369
// https://github.com/TanStack/query/issues/8677
370
370
test ( 'should not infinite loop when hydrating promises that resolve to errors' , async ( ) => {
371
+ const originalHydrate = coreModule . hydrate
371
372
const hydrateSpy = vi . spyOn ( coreModule , 'hydrate' )
372
373
let hydrationCount = 0
373
374
hydrateSpy . mockImplementation ( ( ...args : Parameters < typeof hydrate > ) => {
@@ -379,9 +380,19 @@ describe('React hydration', () => {
379
380
// logic in HydrationBoundary is not working as expected.
380
381
throw new Error ( 'Too many hydrations detected' )
381
382
}
382
- return hydrate ( ...args )
383
+ return originalHydrate ( ...args )
383
384
} )
384
385
386
+ // For the bug to trigger, there needs to already be a query in the cache,
387
+ // with a dataUpdatedAt earlier than the dehydratedAt of the next query
388
+ const clientQueryClient = new QueryClient ( )
389
+ await clientQueryClient . prefetchQuery ( {
390
+ queryKey : [ 'promise' ] ,
391
+ queryFn : ( ) => 'existing' ,
392
+ } )
393
+
394
+ await vi . advanceTimersByTimeAsync ( 100 )
395
+
385
396
const prefetchQueryClient = new QueryClient ( {
386
397
defaultOptions : {
387
398
dehydrate : {
@@ -393,26 +404,21 @@ describe('React hydration', () => {
393
404
queryKey : [ 'promise' ] ,
394
405
queryFn : async ( ) => {
395
406
await sleep ( 10 )
396
- return Promise . reject ( 'Query failed' )
407
+ throw new Error ( 'Query failed' )
397
408
} ,
398
409
} )
399
410
400
411
const dehydratedState = dehydrate ( prefetchQueryClient )
401
412
402
- // Avoid redacted error in test
403
- dehydratedState . queries [ 0 ] ?. promise ?. catch ( ( ) => { } )
404
- await vi . advanceTimersByTimeAsync ( 10 )
413
+ function ignore ( ) {
414
+ // Ignore redacted unhandled rejection
415
+ }
416
+ process . addListener ( 'unhandledRejection' , ignore )
417
+
405
418
// Mimic what React/our synchronous thenable does for already rejected promises
406
419
// @ts -expect-error
407
420
dehydratedState . queries [ 0 ] . promise . status = 'failure'
408
421
409
- // For the bug to trigger, there needs to already be a query in the cache
410
- const queryClient = new QueryClient ( )
411
- await queryClient . prefetchQuery ( {
412
- queryKey : [ 'promise' ] ,
413
- queryFn : ( ) => 'existing' ,
414
- } )
415
-
416
422
function Page ( ) {
417
423
const { data } = useQuery ( {
418
424
queryKey : [ 'promise' ] ,
@@ -426,7 +432,7 @@ describe('React hydration', () => {
426
432
}
427
433
428
434
const rendered = render (
429
- < QueryClientProvider client = { queryClient } >
435
+ < QueryClientProvider client = { clientQueryClient } >
430
436
< HydrationBoundary state = { dehydratedState } >
431
437
< Page />
432
438
</ HydrationBoundary >
@@ -436,8 +442,10 @@ describe('React hydration', () => {
436
442
expect ( rendered . getByText ( 'existing' ) ) . toBeInTheDocument ( )
437
443
await vi . advanceTimersByTimeAsync ( 10 )
438
444
expect ( rendered . getByText ( 'new' ) ) . toBeInTheDocument ( )
445
+
446
+ process . removeListener ( 'unhandledRejection' , ignore )
439
447
hydrateSpy . mockRestore ( )
440
448
prefetchQueryClient . clear ( )
441
- queryClient . clear ( )
449
+ clientQueryClient . clear ( )
442
450
} )
443
451
} )
0 commit comments