@@ -1725,6 +1725,26 @@ function unblockSuspenseListRow(
17251725 }
17261726}
17271727
1728+ function trackPostponedSuspenseListRow (
1729+ request : Request ,
1730+ trackedPostpones : PostponedHoles ,
1731+ postponedRow : null | SuspenseListRow ,
1732+ ) : void {
1733+ // TODO: Because we unconditionally call this, it will be called by finishedTask
1734+ // and so ends up recursive which can lead to stack overflow for very long lists.
1735+ if ( postponedRow !== null ) {
1736+ const postponedBoundaries = postponedRow . boundaries ;
1737+ if ( postponedBoundaries !== null ) {
1738+ postponedRow . boundaries = null ;
1739+ for ( let i = 0 ; i < postponedBoundaries . length ; i ++ ) {
1740+ const postponedBoundary = postponedBoundaries [ i ] ;
1741+ trackPostponedBoundary ( request , trackedPostpones , postponedBoundary ) ;
1742+ finishedTask ( request , postponedBoundary , null , null ) ;
1743+ }
1744+ }
1745+ }
1746+ }
1747+
17281748function tryToResolveTogetherRow (
17291749 request : Request ,
17301750 togetherRow : SuspenseListRow ,
@@ -3774,6 +3794,49 @@ function renderChildrenArray(
37743794 }
37753795}
37763796
3797+ function trackPostponedBoundary (
3798+ request : Request ,
3799+ trackedPostpones : PostponedHoles ,
3800+ boundary : SuspenseBoundary ,
3801+ ) : ReplaySuspenseBoundary {
3802+ boundary . status = POSTPONED ;
3803+ // We need to eagerly assign it an ID because we'll need to refer to
3804+ // it before flushing and we know that we can't inline it.
3805+ boundary . rootSegmentID = request . nextSegmentId ++ ;
3806+
3807+ const boundaryKeyPath = boundary . trackedContentKeyPath ;
3808+ if ( boundaryKeyPath === null ) {
3809+ throw new Error (
3810+ 'It should not be possible to postpone at the root. This is a bug in React.' ,
3811+ ) ;
3812+ }
3813+
3814+ const fallbackReplayNode = boundary . trackedFallbackNode ;
3815+
3816+ const children : Array < ReplayNode > = [];
3817+ const boundaryNode: void | ReplayNode =
3818+ trackedPostpones.workingMap.get(boundaryKeyPath);
3819+ if (boundaryNode === undefined) {
3820+ const suspenseBoundary : ReplaySuspenseBoundary = [
3821+ boundaryKeyPath [ 1 ] ,
3822+ boundaryKeyPath [ 2 ] ,
3823+ children ,
3824+ null ,
3825+ fallbackReplayNode ,
3826+ boundary . rootSegmentID ,
3827+ ] ;
3828+ trackedPostpones . workingMap . set ( boundaryKeyPath , suspenseBoundary ) ;
3829+ addToReplayParent ( suspenseBoundary , boundaryKeyPath [ 0 ] , trackedPostpones ) ;
3830+ return suspenseBoundary ;
3831+ } else {
3832+ // Upgrade to ReplaySuspenseBoundary.
3833+ const suspenseBoundary : ReplaySuspenseBoundary = ( boundaryNode : any ) ;
3834+ suspenseBoundary [ 4 ] = fallbackReplayNode ;
3835+ suspenseBoundary [ 5 ] = boundary . rootSegmentID ;
3836+ return suspenseBoundary ;
3837+ }
3838+ }
3839+
37773840function trackPostpone (
37783841 request : Request ,
37793842 trackedPostpones : PostponedHoles ,
@@ -3796,22 +3859,12 @@ function trackPostpone(
37963859 }
37973860
37983861 if ( boundary !== null && boundary . status === PENDING ) {
3799- boundary . status = POSTPONED ;
3800- // We need to eagerly assign it an ID because we'll need to refer to
3801- // it before flushing and we know that we can't inline it.
3802- boundary . rootSegmentID = request . nextSegmentId ++ ;
3803-
3804- const boundaryKeyPath = boundary . trackedContentKeyPath ;
3805- if ( boundaryKeyPath === null ) {
3806- throw new Error (
3807- 'It should not be possible to postpone at the root. This is a bug in React.' ,
3808- ) ;
3809- }
3810-
3811- const fallbackReplayNode = boundary . trackedFallbackNode ;
3812-
3813- const children : Array < ReplayNode > = [];
3814- if (boundaryKeyPath === keyPath && task . childIndex === - 1 ) {
3862+ const boundaryNode = trackPostponedBoundary (
3863+ request ,
3864+ trackedPostpones ,
3865+ boundary ,
3866+ ) ;
3867+ if ( boundary . trackedContentKeyPath === keyPath && task . childIndex === - 1 ) {
38153868 // Assign ID
38163869 if ( segment . id === - 1 ) {
38173870 if ( segment . parentFlushed ) {
@@ -3823,39 +3876,10 @@ function trackPostpone(
38233876 }
38243877 }
38253878 // We postponed directly inside the Suspense boundary so we mark this for resuming.
3826- const boundaryNode : ReplaySuspenseBoundary = [
3827- boundaryKeyPath [ 1 ] ,
3828- boundaryKeyPath [ 2 ] ,
3829- children ,
3830- segment . id ,
3831- fallbackReplayNode ,
3832- boundary . rootSegmentID ,
3833- ] ;
3834- trackedPostpones . workingMap . set ( boundaryKeyPath , boundaryNode ) ;
3835- addToReplayParent ( boundaryNode , boundaryKeyPath [ 0 ] , trackedPostpones ) ;
3879+ boundaryNode [ 3 ] = segment . id ;
38363880 return ;
3837- } else {
3838- let boundaryNode : void | ReplayNode =
3839- trackedPostpones . workingMap . get ( boundaryKeyPath ) ;
3840- if ( boundaryNode === undefined ) {
3841- boundaryNode = [
3842- boundaryKeyPath [ 1 ] ,
3843- boundaryKeyPath [ 2 ] ,
3844- children ,
3845- null ,
3846- fallbackReplayNode ,
3847- boundary . rootSegmentID ,
3848- ] ;
3849- trackedPostpones . workingMap . set ( boundaryKeyPath , boundaryNode ) ;
3850- addToReplayParent ( boundaryNode , boundaryKeyPath [ 0 ] , trackedPostpones ) ;
3851- } else {
3852- // Upgrade to ReplaySuspenseBoundary.
3853- const suspenseBoundary : ReplaySuspenseBoundary = ( boundaryNode : any ) ;
3854- suspenseBoundary [ 4 ] = fallbackReplayNode ;
3855- suspenseBoundary [ 5 ] = boundary . rootSegmentID ;
3856- }
3857- // Fall through to add the child node.
38583881 }
3882+ // Otherwise, fall through to add the child node.
38593883 }
38603884
38613885 // We know that this will leave a hole so we might as well assign an ID now.
@@ -4941,7 +4965,18 @@ function finishedTask(
49414965 } else if ( boundary . status === POSTPONED ) {
49424966 const boundaryRow = boundary . row ;
49434967 if ( boundaryRow !== null ) {
4968+ if ( request . trackedPostpones !== null ) {
4969+ // If this boundary is postponed, then we need to also postpone any blocked boundaries
4970+ // in the next row.
4971+ trackPostponedSuspenseListRow (
4972+ request ,
4973+ request . trackedPostpones ,
4974+ boundaryRow . next ,
4975+ ) ;
4976+ }
49444977 if ( -- boundaryRow . pendingTasks === 0 ) {
4978+ // This is really unnecessary since we've already postponed the boundaries but
4979+ // for pairity with other track+finish paths. We might end up using the hoisting.
49454980 finishSuspenseListRow ( request , boundaryRow ) ;
49464981 }
49474982 }
0 commit comments