@@ -560,6 +560,86 @@ describe('ReactDOMServerPartialHydration', () => {
560
560
expect ( container . textContent ) . toBe ( 'Hi Hi' ) ;
561
561
} ) ;
562
562
563
+ it ( 'hydrates first if props changed but we are able to resolve within a timeout' , async ( ) => {
564
+ let suspend = false ;
565
+ let resolve ;
566
+ let promise = new Promise ( resolvePromise => ( resolve = resolvePromise ) ) ;
567
+ let ref = React . createRef ( ) ;
568
+
569
+ function Child ( { text} ) {
570
+ if ( suspend ) {
571
+ throw promise ;
572
+ } else {
573
+ return text ;
574
+ }
575
+ }
576
+
577
+ function App ( { text, className} ) {
578
+ return (
579
+ < div >
580
+ < Suspense fallback = "Loading..." >
581
+ < span ref = { ref } className = { className } >
582
+ < Child text = { text } />
583
+ </ span >
584
+ </ Suspense >
585
+ </ div >
586
+ ) ;
587
+ }
588
+
589
+ suspend = false ;
590
+ let finalHTML = ReactDOMServer . renderToString (
591
+ < App text = "Hello" className = "hello" /> ,
592
+ ) ;
593
+ let container = document . createElement ( 'div' ) ;
594
+ container . innerHTML = finalHTML ;
595
+
596
+ let span = container . getElementsByTagName ( 'span' ) [ 0 ] ;
597
+
598
+ // On the client we don't have all data yet but we want to start
599
+ // hydrating anyway.
600
+ suspend = true ;
601
+ let root = ReactDOM . unstable_createRoot ( container , { hydrate : true } ) ;
602
+ root . render ( < App text = "Hello" className = "hello" /> ) ;
603
+ Scheduler . unstable_flushAll ( ) ;
604
+ jest . runAllTimers ( ) ;
605
+
606
+ expect ( ref . current ) . toBe ( null ) ;
607
+ expect ( container . textContent ) . toBe ( 'Hello' ) ;
608
+
609
+ // Render an update with a long timeout.
610
+ React . unstable_withSuspenseConfig (
611
+ ( ) => root . render ( < App text = "Hi" className = "hi" /> ) ,
612
+ { timeoutMs : 5000 } ,
613
+ ) ;
614
+
615
+ // This shouldn't force the fallback yet.
616
+ Scheduler . unstable_flushAll ( ) ;
617
+
618
+ expect ( ref . current ) . toBe ( null ) ;
619
+ expect ( container . textContent ) . toBe ( 'Hello' ) ;
620
+
621
+ // Resolving the promise so that rendering can complete.
622
+ suspend = false ;
623
+ resolve ( ) ;
624
+ await promise ;
625
+
626
+ // This should first complete the hydration and then flush the update onto the hydrated state.
627
+ Scheduler . unstable_flushAll ( ) ;
628
+ jest . runAllTimers ( ) ;
629
+
630
+ // The new span should be the same since we should have successfully hydrated
631
+ // before changing it.
632
+ let newSpan = container . getElementsByTagName ( 'span' ) [ 0 ] ;
633
+ expect ( span ) . toBe ( newSpan ) ;
634
+
635
+ // We should now have fully rendered with a ref on the new span.
636
+ expect ( ref . current ) . toBe ( span ) ;
637
+ expect ( container . textContent ) . toBe ( 'Hi' ) ;
638
+ // If we ended up hydrating the existing content, we won't have properly
639
+ // patched up the tree, which might mean we haven't patched the className.
640
+ expect ( span . className ) . toBe ( 'hi' ) ;
641
+ } ) ;
642
+
563
643
it ( 'blocks the update to hydrate first if context has changed' , async ( ) => {
564
644
let suspend = false ;
565
645
let resolve ;
0 commit comments