@@ -3409,4 +3409,108 @@ describe('ReactSuspenseWithNoopRenderer', () => {
34093409 ) ;
34103410 } ,
34113411 ) ;
3412+
3413+ it ( 'a high pri update can unhide a boundary that suspended at a different level' , async ( ) => {
3414+ const { useState, useEffect} = React ;
3415+ const root = ReactNoop . createRoot ( ) ;
3416+
3417+ let setOuterText ;
3418+ function Parent ( { step} ) {
3419+ const [ text , _setText ] = useState ( 'A' ) ;
3420+ setOuterText = _setText ;
3421+ return (
3422+ < >
3423+ < Text text = { 'Outer: ' + text + step } />
3424+ < Suspense fallback = { < Text text = "Loading..." /> } >
3425+ < Child step = { step } outerText = { text } />
3426+ </ Suspense >
3427+ </ >
3428+ ) ;
3429+ }
3430+
3431+ let setInnerText ;
3432+ function Child ( { step, outerText} ) {
3433+ const [ text , _setText ] = useState ( 'A' ) ;
3434+ setInnerText = _setText ;
3435+
3436+ // This will log if the component commits in an inconsistent state
3437+ useEffect ( ( ) => {
3438+ if ( text === outerText ) {
3439+ Scheduler . unstable_yieldValue ( 'Commit Child' ) ;
3440+ } else {
3441+ Scheduler . unstable_yieldValue (
3442+ 'FIXME: Texts are inconsistent (tearing)' ,
3443+ ) ;
3444+ }
3445+ } , [ text , outerText ] ) ;
3446+
3447+ return (
3448+ < >
3449+ < AsyncText text = { 'Inner: ' + text + step } />
3450+ </ >
3451+ ) ;
3452+ }
3453+
3454+ // These always update simultaneously. They must be consistent.
3455+ function setText ( text ) {
3456+ setOuterText ( text ) ;
3457+ setInnerText ( text ) ;
3458+ }
3459+
3460+ // Mount an initial tree. Resolve A so that it doesn't suspend.
3461+ await resolveText ( 'Inner: A0' ) ;
3462+ await ReactNoop . act ( async ( ) => {
3463+ root . render ( < Parent step = { 0 } /> ) ;
3464+ } ) ;
3465+ expect ( Scheduler ) . toHaveYielded ( [ 'Outer: A0' , 'Inner: A0' , 'Commit Child' ] ) ;
3466+ expect ( root ) . toMatchRenderedOutput (
3467+ < >
3468+ < span prop = "Outer: A0" />
3469+ < span prop = "Inner: A0" />
3470+ </ > ,
3471+ ) ;
3472+
3473+ // Update. This causes the inner component to suspend.
3474+ await ReactNoop . act ( async ( ) => {
3475+ setText ( 'B' ) ;
3476+ } ) ;
3477+ expect ( Scheduler ) . toHaveYielded ( [
3478+ 'Outer: B0' ,
3479+ 'Suspend! [Inner: B0]' ,
3480+ 'Loading...' ,
3481+ ] ) ;
3482+ // Commit the placeholder
3483+ await advanceTimers ( 250 ) ;
3484+ expect ( root ) . toMatchRenderedOutput (
3485+ < >
3486+ < span prop = "Outer: B0" />
3487+ < span hidden = { true } prop = "Inner: A0" />
3488+ < span prop = "Loading..." />
3489+ </ > ,
3490+ ) ;
3491+
3492+ // Schedule a high pri update on the parent. This will unblock the content.
3493+ await resolveText ( 'Inner: B1' ) ;
3494+ await ReactNoop . act ( async ( ) => {
3495+ ReactNoop . discreteUpdates ( ( ) => {
3496+ root . render ( < Parent step = { 1 } /> ) ;
3497+ } ) ;
3498+ } ) ;
3499+
3500+ expect ( Scheduler ) . toHaveYielded ( [
3501+ // First the outer part of the tree updates, at high pri.
3502+ 'Outer: B1' ,
3503+ 'Loading...' ,
3504+
3505+ // Then we retry the boundary.
3506+ 'Inner: B1' ,
3507+ 'Commit Child' ,
3508+ ] ) ;
3509+ expect ( root ) . toMatchRenderedOutput (
3510+ < >
3511+ < span prop = "Outer: B1" />
3512+ < span prop = "Inner: B1" />
3513+ </ > ,
3514+ ) ;
3515+ } ) ;
34123516} ) ;
0 commit comments