Skip to content

Commit 714ca3b

Browse files
committed
Use currentEventTransitionLane
1 parent 638cd73 commit 714ca3b

File tree

4 files changed

+67
-19
lines changed

4 files changed

+67
-19
lines changed

packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ let ReactDOMClient;
1616
let Scheduler;
1717
let act;
1818
let waitForAll;
19+
let waitFor;
1920
let assertLog;
2021

2122
const setUntrackedInputValue = Object.getOwnPropertyDescriptor(
@@ -37,6 +38,7 @@ describe('ReactDOMFiberAsync', () => {
3738

3839
const InternalTestUtils = require('internal-test-utils');
3940
waitForAll = InternalTestUtils.waitForAll;
41+
waitFor = InternalTestUtils.waitFor;
4042
assertLog = InternalTestUtils.assertLog;
4143

4244
document.body.appendChild(container);
@@ -567,7 +569,7 @@ describe('ReactDOMFiberAsync', () => {
567569
expect(container.textContent).toBe('new');
568570
});
569571

570-
it('should synchronously render the transition lane in a popState', async () => {
572+
it('should synchronously render the transition lane scheduled in a popState', async () => {
571573
function App() {
572574
const [syncState, setSyncState] = React.useState(false);
573575
const [hasNavigated, setHasNavigated] = React.useState(false);
@@ -579,11 +581,8 @@ describe('ReactDOMFiberAsync', () => {
579581
setSyncState(true);
580582

581583
// Jest is not emulating window.event correctly in the microtask
582-
const currentEvent = window.event;
583-
window.event = currentEvent;
584-
queueMicrotask(() => {
585-
window.event = null;
586-
});
584+
// TODO: this causes `window.event` to always be popState in this test file
585+
window.event = window.event;
587586
}
588587
React.useEffect(() => {
589588
window.addEventListener('popstate', onPopstate);
@@ -607,4 +606,45 @@ describe('ReactDOMFiberAsync', () => {
607606

608607
root.unmount();
609608
});
609+
610+
it('should not flush transition lanes if there is no transition scheduled in popState', async () => {
611+
let _setHasNavigated;
612+
function App({shouldTransition}) {
613+
const [syncState, setSyncState] = React.useState(false);
614+
const [hasNavigated, setHasNavigated] = React.useState(false);
615+
_setHasNavigated = setHasNavigated;
616+
function onPopstate() {
617+
setSyncState(true);
618+
619+
// Jest is not emulating window.event correctly in the microtask
620+
window.event = window.event;
621+
}
622+
623+
React.useEffect(() => {
624+
window.addEventListener('popstate', onPopstate);
625+
return () => window.removeEventListener('popstate', onPopstate);
626+
}, []);
627+
628+
Scheduler.log(`render:${hasNavigated}/${syncState}`);
629+
630+
return null;
631+
}
632+
const root = ReactDOMClient.createRoot(container);
633+
await act(async () => {
634+
root.render(<App />);
635+
});
636+
assertLog(['render:false/false']);
637+
638+
// Trigger a transition update to be scheduled
639+
React.startTransition(() => {
640+
_setHasNavigated(true);
641+
})
642+
643+
// The popState should not flush the transition update
644+
const popStateEvent = new Event('popstate');
645+
window.dispatchEvent(popStateEvent);
646+
await waitFor(['render:false/true']);
647+
648+
root.unmount();
649+
});
610650
});

packages/react-reconciler/src/ReactFiberLane.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -471,10 +471,6 @@ export function getLanesToRetrySynchronouslyOnError(
471471
return NoLanes;
472472
}
473473

474-
export function getTransitionLanes(lanes: Lanes): Lanes {
475-
return lanes & TransitionLanes;
476-
}
477-
478474
export function includesSyncLane(lanes: Lanes): boolean {
479475
return (lanes & (SyncLane | SyncHydrationLane)) !== NoLanes;
480476
}

packages/react-reconciler/src/ReactFiberRootScheduler.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,9 @@ import {
1919
getHighestPriorityLane,
2020
getNextLanes,
2121
includesSyncLane,
22-
includesTransitionLane,
2322
markStarvedLanesAsExpired,
2423
markRootEntangled,
2524
mergeLanes,
26-
getTransitionLanes,
2725
} from './ReactFiberLane';
2826
import {
2927
CommitContext,
@@ -80,6 +78,8 @@ let mightHavePendingSyncWork: boolean = false;
8078

8179
let isFlushingWork: boolean = false;
8280

81+
let currentEventTransitionLane: Lane = NoLanes;
82+
8383
export function ensureRootIsScheduled(root: FiberRoot): void {
8484
// This function is called whenever a root receives an update. It does two
8585
// things 1) it ensures the root is in the root schedule, and 2) it ensures
@@ -237,12 +237,14 @@ function processRootScheduleInMicrotask() {
237237
let root = firstScheduledRoot;
238238
while (root !== null) {
239239
const next = root.next;
240-
if (includesTransitionLane(root.pendingLanes) && shouldAttemptEagerTransition()) {
240+
241+
if (currentEventTransitionLane !== NoLane && shouldAttemptEagerTransition()) {
241242
markRootEntangled(
242243
root,
243-
mergeLanes(getTransitionLanes(root.pendingLanes), SyncLane),
244+
mergeLanes(currentEventTransitionLane, SyncLane),
244245
);
245246
}
247+
246248
const nextLanes = scheduleTaskForRootDuringMicrotask(root, currentTime);
247249
if (nextLanes === NoLane) {
248250
// This root has no more pending work. Remove it from the schedule. To
@@ -272,6 +274,8 @@ function processRootScheduleInMicrotask() {
272274
root = next;
273275
}
274276

277+
currentEventTransitionLane = NoLane;
278+
275279
// At the end of the microtask, flush any pending synchronous work. This has
276280
// to come at the end, because it does actual rendering work that might throw.
277281
flushSyncWorkOnAllRoots();
@@ -477,3 +481,11 @@ function scheduleImmediateTask(cb: () => mixed) {
477481
Scheduler_scheduleCallback(ImmediateSchedulerPriority, cb);
478482
}
479483
}
484+
485+
export function getCurrentEventTransitionLane(): Lane {
486+
return currentEventTransitionLane;
487+
}
488+
489+
export function setCurrentEventTransitionLane(lane: Lane): void {
490+
currentEventTransitionLane = lane;
491+
}

packages/react-reconciler/src/ReactFiberWorkLoop.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ import {
280280
flushSyncWorkOnAllRoots,
281281
flushSyncWorkOnLegacyRootsOnly,
282282
getContinuationForRoot,
283+
getCurrentEventTransitionLane,
284+
setCurrentEventTransitionLane,
283285
} from './ReactFiberRootScheduler';
284286

285287
const ceil = Math.ceil;
@@ -589,7 +591,6 @@ let rootWithPassiveNestedUpdates: FiberRoot | null = null;
589591
// event times as simultaneous, even if the actual clock time has advanced
590592
// between the first and second call.
591593
let currentEventTime: number = NoTimestamp;
592-
let currentEventTransitionLane: Lanes = NoLanes;
593594

594595
let isRunningInsertionEffect = false;
595596

@@ -662,11 +663,11 @@ export function requestUpdateLane(fiber: Fiber): Lane {
662663
// The trick we use is to cache the first of each of these inputs within an
663664
// event. Then reset the cached values once we can be sure the event is
664665
// over. Our heuristic for that is whenever we enter a concurrent work loop.
665-
if (currentEventTransitionLane === NoLane) {
666+
if (getCurrentEventTransitionLane() === NoLane) {
666667
// All transitions within the same event are assigned the same lane.
667-
currentEventTransitionLane = claimNextTransitionLane();
668+
setCurrentEventTransitionLane(claimNextTransitionLane());
668669
}
669-
return currentEventTransitionLane;
670+
return getCurrentEventTransitionLane();
670671
}
671672

672673
// Updates originating inside certain React methods, like flushSync, have
@@ -879,7 +880,6 @@ export function performConcurrentWorkOnRoot(
879880
// Since we know we're in a React event, we can clear the current
880881
// event time. The next update will compute a new event time.
881882
currentEventTime = NoTimestamp;
882-
currentEventTransitionLane = NoLanes;
883883

884884
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
885885
throw new Error('Should not already be working.');

0 commit comments

Comments
 (0)