@@ -92,6 +92,7 @@ export const DefaultLanes: Lanes = /* */ 0b0000000000000000000
92
92
93
93
const TransitionHydrationLane : Lane = /* */ 0b0000000000000000001000000000000 ;
94
94
const TransitionLanes : Lanes = /* */ 0b0000000001111111110000000000000 ;
95
+ const SomeTransitionLane : Lane = /* */ 0b0000000000000000010000000000000 ;
95
96
96
97
const RetryLanes : Lanes = /* */ 0b0000011110000000000000000000000 ;
97
98
@@ -110,6 +111,9 @@ export const NoTimestamp = -1;
110
111
111
112
let currentUpdateLanePriority : LanePriority = NoLanePriority ;
112
113
114
+ let nextTransitionLane : Lane = SomeTransitionLane ;
115
+ let nextRetryLane : Lane = SomeRetryLane ;
116
+
113
117
export function getCurrentUpdateLanePriority ( ) : LanePriority {
114
118
return currentUpdateLanePriority ;
115
119
}
@@ -338,6 +342,11 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
338
342
// entanglement is usually "best effort": we'll try our best to render the
339
343
// lanes in the same batch, but it's not worth throwing out partially
340
344
// completed work in order to do it.
345
+ // TODO: Reconsider this. The counter-argument is that the partial work
346
+ // represents an intermediate state, which we don't want to show to the user.
347
+ // And by spending extra time finishing it, we're increasing the amount of
348
+ // time it takes to show the final state, which is what they are actually
349
+ // waiting for.
341
350
//
342
351
// For those exceptions where entanglement is semantically important, like
343
352
// useMutableSource, we should ensure that there is no partial work at the
@@ -547,34 +556,23 @@ export function findUpdateLane(
547
556
) ;
548
557
}
549
558
550
- // To ensure consistency across multiple updates in the same event, this should
551
- // be pure function, so that it always returns the same lane for given inputs.
552
- export function findTransitionLane ( wipLanes : Lanes , pendingLanes : Lanes ) : Lane {
553
- // First look for lanes that are completely unclaimed, i.e. have no
554
- // pending work.
555
- let lane = pickArbitraryLane ( TransitionLanes & ~ pendingLanes ) ;
556
- if ( lane === NoLane ) {
557
- // If all lanes have pending work, look for a lane that isn't currently
558
- // being worked on.
559
- lane = pickArbitraryLane ( TransitionLanes & ~ wipLanes ) ;
560
- if ( lane === NoLane ) {
561
- // If everything is being worked on, pick any lane. This has the
562
- // effect of interrupting the current work-in-progress.
563
- lane = pickArbitraryLane ( TransitionLanes ) ;
564
- }
559
+ export function claimNextTransitionLane ( ) : Lane {
560
+ // Cycle through the lanes, assigning each new transition to the next lane.
561
+ // In most cases, this means every transition gets its own lane, until we
562
+ // run out of lanes and cycle back to the beginning.
563
+ const lane = nextTransitionLane ;
564
+ nextTransitionLane <<= 1 ;
565
+ if ( ( nextTransitionLane & TransitionLanes ) === 0 ) {
566
+ nextTransitionLane = SomeTransitionLane ;
565
567
}
566
568
return lane ;
567
569
}
568
570
569
- // To ensure consistency across multiple updates in the same event, this should
570
- // be pure function, so that it always returns the same lane for given inputs.
571
- export function findRetryLane ( wipLanes : Lanes ) : Lane {
572
- // This is a fork of `findUpdateLane` designed specifically for Suspense
573
- // "retries" — a special update that attempts to flip a Suspense boundary
574
- // from its placeholder state to its primary/resolved state.
575
- let lane = pickArbitraryLane ( RetryLanes & ~ wipLanes ) ;
576
- if ( lane === NoLane ) {
577
- lane = pickArbitraryLane ( RetryLanes ) ;
571
+ export function claimNextRetryLane ( ) : Lane {
572
+ const lane = nextRetryLane ;
573
+ nextRetryLane <<= 1 ;
574
+ if ( ( nextRetryLane & RetryLanes ) === 0 ) {
575
+ nextRetryLane = SomeRetryLane ;
578
576
}
579
577
return lane ;
580
578
}
@@ -761,16 +759,32 @@ export function markRootFinished(root: FiberRoot, remainingLanes: Lanes) {
761
759
}
762
760
763
761
export function markRootEntangled ( root : FiberRoot , entangledLanes : Lanes ) {
764
- root . entangledLanes |= entangledLanes ;
762
+ // In addition to entangling each of the given lanes with each other, we also
763
+ // have to consider _transitive_ entanglements. For each lane that is already
764
+ // entangled with *any* of the given lanes, that lane is now transitively
765
+ // entangled with *all* the given lanes.
766
+ //
767
+ // Translated: If C is entangled with A, then entangling A with B also
768
+ // entangles C with B.
769
+ //
770
+ // If this is hard to grasp, it might help to intentionally break this
771
+ // function and look at the tests that fail in ReactTransition-test.js. Try
772
+ // commenting out one of the conditions below.
765
773
774
+ const rootEntangledLanes = ( root . entangledLanes |= entangledLanes ) ;
766
775
const entanglements = root . entanglements ;
767
- let lanes = entangledLanes ;
768
- while ( lanes > 0 ) {
776
+ let lanes = rootEntangledLanes ;
777
+ while ( lanes ) {
769
778
const index = pickArbitraryLaneIndex ( lanes ) ;
770
779
const lane = 1 << index ;
771
-
772
- entanglements [ index ] |= entangledLanes ;
773
-
780
+ if (
781
+ // Is this one of the newly entangled lanes?
782
+ ( lane & entangledLanes ) |
783
+ // Is this lane transitively entangled with the newly entangled lanes?
784
+ ( entanglements [ index ] & entangledLanes )
785
+ ) {
786
+ entanglements [ index ] |= entangledLanes ;
787
+ }
774
788
lanes &= ~ lane ;
775
789
}
776
790
}
0 commit comments