88 QueryClient ,
99 QueryClientProvider ,
1010 dehydrate ,
11- hydrate ,
1211 useQuery ,
1312} from '..'
13+ import type { hydrate } from '@tanstack/query-core'
1414
1515describe ( 'React hydration' , ( ) => {
1616 let stringifiedState : string
@@ -368,6 +368,7 @@ describe('React hydration', () => {
368368
369369 // https://github.com/TanStack/query/issues/8677
370370 test ( 'should not infinite loop when hydrating promises that resolve to errors' , async ( ) => {
371+ const originalHydrate = coreModule . hydrate
371372 const hydrateSpy = vi . spyOn ( coreModule , 'hydrate' )
372373 let hydrationCount = 0
373374 hydrateSpy . mockImplementation ( ( ...args : Parameters < typeof hydrate > ) => {
@@ -379,9 +380,19 @@ describe('React hydration', () => {
379380 // logic in HydrationBoundary is not working as expected.
380381 throw new Error ( 'Too many hydrations detected' )
381382 }
382- return hydrate ( ...args )
383+ return originalHydrate ( ...args )
383384 } )
384385
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+
385396 const prefetchQueryClient = new QueryClient ( {
386397 defaultOptions : {
387398 dehydrate : {
@@ -393,26 +404,21 @@ describe('React hydration', () => {
393404 queryKey : [ 'promise' ] ,
394405 queryFn : async ( ) => {
395406 await sleep ( 10 )
396- return Promise . reject ( 'Query failed' )
407+ throw new Error ( 'Query failed' )
397408 } ,
398409 } )
399410
400411 const dehydratedState = dehydrate ( prefetchQueryClient )
401412
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+
405418 // Mimic what React/our synchronous thenable does for already rejected promises
406419 // @ts -expect-error
407420 dehydratedState . queries [ 0 ] . promise . status = 'failure'
408421
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-
416422 function Page ( ) {
417423 const { data } = useQuery ( {
418424 queryKey : [ 'promise' ] ,
@@ -426,7 +432,7 @@ describe('React hydration', () => {
426432 }
427433
428434 const rendered = render (
429- < QueryClientProvider client = { queryClient } >
435+ < QueryClientProvider client = { clientQueryClient } >
430436 < HydrationBoundary state = { dehydratedState } >
431437 < Page />
432438 </ HydrationBoundary >
@@ -436,8 +442,10 @@ describe('React hydration', () => {
436442 expect ( rendered . getByText ( 'existing' ) ) . toBeInTheDocument ( )
437443 await vi . advanceTimersByTimeAsync ( 10 )
438444 expect ( rendered . getByText ( 'new' ) ) . toBeInTheDocument ( )
445+
446+ process . removeListener ( 'unhandledRejection' , ignore )
439447 hydrateSpy . mockRestore ( )
440448 prefetchQueryClient . clear ( )
441- queryClient . clear ( )
449+ clientQueryClient . clear ( )
442450 } )
443451} )
0 commit comments