[Fiber] Stash the entangled async action lane on currentEventTransitionLane (#33188)

When we're entangled with an async action lane we use that lane instead
of the currentEventTransitionLane. Conversely, if we start a new async
action lane we reuse the currentEventTransitionLane.

So they're basically supposed to be in sync but they're not if you
resolve the async action and then schedule new stuff in the same event.
Then you end up with two transitions in the same event with different
lanes.

By stashing it like this we fix that but it also gives us an opportunity
to check just the currentEventTransitionLane to see if this event
scheduled any regular Transition updates or Async Transitions.
This commit is contained in:
Sebastian Markbåge
2025-05-13 15:20:59 -04:00
committed by GitHub
parent 676f0879f3
commit 0cac32d60d
2 changed files with 11 additions and 10 deletions
+10 -1
View File
@@ -78,6 +78,7 @@ import {
resetNestedUpdateFlag,
syncNestedUpdateFlag,
} from './ReactProfilerTimer';
import {peekEntangledActionLane} from './ReactFiberAsyncAction';
// A linked list of all the roots with pending work. In an idiomatic app,
// there's only a single root, but we do support multi root apps, hence this
@@ -647,7 +648,15 @@ export function requestTransitionLane(
// over. Our heuristic for that is whenever we enter a concurrent work loop.
if (currentEventTransitionLane === NoLane) {
// All transitions within the same event are assigned the same lane.
currentEventTransitionLane = claimNextTransitionLane();
const actionScopeLane = peekEntangledActionLane();
currentEventTransitionLane =
actionScopeLane !== NoLane
? // We're inside an async action scope. Reuse the same lane.
actionScopeLane
: // We may or may not be inside an async action scope. If we are, this
// is the first update in that scope. Either way, we need to get a
// fresh transition lane.
claimNextTransitionLane();
}
return currentEventTransitionLane;
}
+1 -9
View File
@@ -356,7 +356,6 @@ import {
requestTransitionLane,
} from './ReactFiberRootScheduler';
import {getMaskedContext, getUnmaskedContext} from './ReactFiberContext';
import {peekEntangledActionLane} from './ReactFiberAsyncAction';
import {logUncaughtError} from './ReactFiberErrorLogger';
import {
deleteScheduledGesture,
@@ -779,14 +778,7 @@ export function requestUpdateLane(fiber: Fiber): Lane {
transition._updatedFibers.add(fiber);
}
const actionScopeLane = peekEntangledActionLane();
return actionScopeLane !== NoLane
? // We're inside an async action scope. Reuse the same lane.
actionScopeLane
: // We may or may not be inside an async action scope. If we are, this
// is the first update in that scope. Either way, we need to get a
// fresh transition lane.
requestTransitionLane(transition);
return requestTransitionLane(transition);
}
return eventPriorityToLane(resolveUpdatePriority());