Skip to content

Commit

Permalink
Fix: Synchronous popstate transitions (#30759)
Browse files Browse the repository at this point in the history
This is a refactor of the fix in #27505.

When a transition update is scheduled by a popstate event, (i.e. a back/
forward navigation) we attempt to render it synchronously even though
it's a transition, since it's likely the previous page's data is cached.

In #27505, I changed the implementation so that it only "upgrades" the
priority of the transition for a single attempt. If the attempt
suspends, say because the data is not cached after all, from then on it
should be treated as a normal transition.

But it turns out #27505 did not work as intended, because it relied on
marking the root with pending synchronous work (root.pendingLanes),
which was never cleared until the popstate update completed.

The test scenarios I wrote accidentally worked for a different reason
related to suspending the work loop, which I'm currently in the middle
of refactoring.

DiffTrain build for commit ee7f675.
  • Loading branch information
acdlite committed Aug 23, 2024
1 parent 99d43a1 commit 6c99ba7
Show file tree
Hide file tree
Showing 14 changed files with 890 additions and 731 deletions.
2 changes: 1 addition & 1 deletion compiled-rn/VERSION_NATIVE_FB
Original file line number Diff line number Diff line change
@@ -1 +1 @@
19.0.0-native-fb-85fb95cd-20240816
19.0.0-native-fb-ee7f6757-20240823
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<7279cc9cb0c567689cd8628192884a45>>
* @generated SignedSource<<487e4d25726d3b9452e5df47a0ff70f5>>
*/

"use strict";
__DEV__ &&
(function () {
function JSCompiler_object_inline_createNodeMock_1088() {
function JSCompiler_object_inline_createNodeMock_1089() {
return null;
}
function findHook(fiber, id) {
Expand Down Expand Up @@ -1789,28 +1789,41 @@ __DEV__ &&
0 === root.tag &&
(ReactSharedInternals.didScheduleLegacyUpdate = !0);
}
function flushSyncWorkAcrossRoots_impl(onlyLegacy) {
function flushSyncWorkAcrossRoots_impl(syncTransitionLanes, onlyLegacy) {
if (!isFlushingWork && mightHavePendingSyncWork) {
isFlushingWork = !0;
do {
var didPerformSomeWork = !1;
for (var root = firstScheduledRoot; null !== root; ) {
if (!onlyLegacy || 0 === root.tag) {
var workInProgressRootRenderLanes$jscomp$0 =
workInProgressRootRenderLanes;
workInProgressRootRenderLanes$jscomp$0 = getNextLanes(
root,
root === workInProgressRoot
? workInProgressRootRenderLanes$jscomp$0
: 0
);
0 !== (workInProgressRootRenderLanes$jscomp$0 & 3) &&
((didPerformSomeWork = !0),
performSyncWorkOnRoot(
root,
workInProgressRootRenderLanes$jscomp$0
));
}
if (!onlyLegacy || 0 === root.tag)
if (0 !== syncTransitionLanes) {
var pendingLanes = root.pendingLanes;
if (0 === pendingLanes) var nextLanes = 0;
else {
var suspendedLanes = root.suspendedLanes,
pingedLanes = root.pingedLanes;
nextLanes =
(1 << (31 - clz32(42 | syncTransitionLanes) + 1)) - 1;
nextLanes &= pendingLanes & ~(suspendedLanes & ~pingedLanes);
nextLanes =
nextLanes & 201326677
? (nextLanes & 201326677) | 1
: nextLanes
? nextLanes | 2
: 0;
}
0 !== nextLanes &&
((didPerformSomeWork = !0),
performSyncWorkOnRoot(root, nextLanes));
} else
(nextLanes = workInProgressRootRenderLanes),
(nextLanes = getNextLanes(
root,
root === workInProgressRoot ? nextLanes : 0
)),
0 !== (nextLanes & 3) &&
((didPerformSomeWork = !0),
performSyncWorkOnRoot(root, nextLanes));
root = root.next;
}
} while (didPerformSomeWork);
Expand All @@ -1822,6 +1835,7 @@ __DEV__ &&
didScheduleMicrotask_act =
didScheduleMicrotask =
!1;
0 !== currentEventTransitionLane && (currentEventTransitionLane = 0);
for (
var currentTime = now$1(), prev = null, root = firstScheduledRoot;
null !== root;
Expand All @@ -1837,8 +1851,7 @@ __DEV__ &&
0 !== (nextLanes & 3) && (mightHavePendingSyncWork = !0));
root = next;
}
currentEventTransitionLane = 0;
flushSyncWorkAcrossRoots_impl(!1);
flushSyncWorkAcrossRoots_impl(0, !1);
}
function scheduleTaskForRootDuringMicrotask(root, currentTime) {
for (
Expand Down Expand Up @@ -10536,7 +10549,7 @@ __DEV__ &&
0 !== (fiber.mode & 1) ||
ReactSharedInternals.isBatchingLegacy ||
((workInProgressRootRenderTargetTime = now$1() + RENDER_TIMEOUT_MS),
flushSyncWorkAcrossRoots_impl(!0));
flushSyncWorkAcrossRoots_impl(0, !0));
}
function performConcurrentWorkOnRoot(root, didTimeout) {
nestedUpdateScheduled = currentUpdateIsNested = !1;
Expand Down Expand Up @@ -10861,12 +10874,12 @@ __DEV__ &&
(ReactSharedInternals.T = prevTransition),
(executionContext = prevExecutionContext),
(executionContext & (RenderContext | CommitContext)) === NoContext &&
flushSyncWorkAcrossRoots_impl(!1);
flushSyncWorkAcrossRoots_impl(0, !1);
}
}
function flushSyncWork() {
return (executionContext & (RenderContext | CommitContext)) === NoContext
? (flushSyncWorkAcrossRoots_impl(!1), !1)
? (flushSyncWorkAcrossRoots_impl(0, !1), !1)
: !0;
}
function resetWorkInProgressStack() {
Expand Down Expand Up @@ -11549,7 +11562,7 @@ __DEV__ &&
? nestedUpdateCount++
: ((nestedUpdateCount = 0), (rootWithNestedUpdates = root)))
: (nestedUpdateCount = 0);
flushSyncWorkAcrossRoots_impl(!1);
flushSyncWorkAcrossRoots_impl(0, !1);
markCommitStopped();
return null;
}
Expand Down Expand Up @@ -11679,7 +11692,7 @@ __DEV__ &&
injectedProfilingHooks.markPassiveEffectsStopped();
commitDoubleInvokeEffectsInDEV(priority, !0);
executionContext = prevExecutionContext;
flushSyncWorkAcrossRoots_impl(!1);
flushSyncWorkAcrossRoots_impl(0, !1);
didScheduleUpdateDuringPassiveEffects
? priority === rootWithPassiveNestedUpdates
? nestedPassiveUpdateCount++
Expand Down Expand Up @@ -14925,11 +14938,11 @@ __DEV__ &&
(function () {
var internals = {
bundleType: 1,
version: "19.0.0-native-fb-85fb95cd-20240816",
version: "19.0.0-native-fb-ee7f6757-20240823",
rendererPackageName: "react-test-renderer",
currentDispatcherRef: ReactSharedInternals,
findFiberByHostInstance: getInstanceFromNode,
reconcilerVersion: "19.0.0-native-fb-85fb95cd-20240816"
reconcilerVersion: "19.0.0-native-fb-ee7f6757-20240823"
};
internals.overrideHookState = overrideHookState;
internals.overrideHookStateDeletePath = overrideHookStateDeletePath;
Expand All @@ -14951,7 +14964,7 @@ __DEV__ &&
exports._Scheduler = Scheduler;
exports.act = act;
exports.create = function (element, options) {
var createNodeMock = JSCompiler_object_inline_createNodeMock_1088,
var createNodeMock = JSCompiler_object_inline_createNodeMock_1089,
isConcurrent = !1,
isStrictMode = !1;
"object" === typeof options &&
Expand Down Expand Up @@ -15071,8 +15084,8 @@ __DEV__ &&
executionContext !== NoContext ||
ReactSharedInternals.isBatchingLegacy ||
((workInProgressRootRenderTargetTime = now$1() + RENDER_TIMEOUT_MS),
flushSyncWorkAcrossRoots_impl(!0));
flushSyncWorkAcrossRoots_impl(0, !0));
}
};
exports.version = "19.0.0-native-fb-85fb95cd-20240816";
exports.version = "19.0.0-native-fb-ee7f6757-20240823";
})();
Loading

0 comments on commit 6c99ba7

Please sign in to comment.