Skip to content

Commit df27420

Browse files
committed
Skip finalizeContainerChildren for suspended transition
Work-in-progress idea targeting the same situation as #30513. In React Native, we've found a bug with suspense boundaries reverting to the fallback in the following case. The expected behavior (and behavior on web) is as follows: 1. Render a Suspense boundary on initial mount, resolve the promise, show the content. 2. In a transition, update the content of the existing Suspense boundary, suspending again. 3. The UI should continue to show the previous content 4. Resolve the promise, update to the new content. However on RN, step 3 shows the fallback again. This is unexpected since we're in a transition and the fallback has already revealed. What's happening is that in step 3 we call completeWork() which in turn calls finalizeContainerChildren(), which updates to use the fallback. However, this isn't committed to the screen since we never call commitRoot() (and therefore don't call replaceContainerChildren()). Instead, we detec that the render exited with status RootSuspendedOnDelay and that the lane was transition-only, so we don't actually commit the fallback. However, RN currently commits the content it gets in finalizeContainerChildren(), rather than waiting for replaceContainerChildren() from commitRoot(). The original intent was for finalize...() to do layout, and for replace...() to actually commit the updated tree, which would preserve the web behavior. #30513 is a brute force way to address this by simply moving all the native work from finalize -> replace. What i'm experimenting with in this PR is to skip calling finalizeContainerChildren() if the above conditions hold: if this is a suspended render in a transition, don't bother finalizing children.
1 parent 03072a7 commit df27420

File tree

2 files changed

+12
-1
lines changed

2 files changed

+12
-1
lines changed

packages/react-reconciler/src/ReactFiberCompleteWork.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ import {
153153
getRenderTargetTime,
154154
getWorkInProgressTransitions,
155155
shouldRemainOnPreviousScreen,
156+
renderDidSuspendWithDelay,
156157
} from './ReactFiberWorkLoop';
157158
import {
158159
OffscreenLane,
@@ -161,6 +162,7 @@ import {
161162
includesSomeLane,
162163
mergeLanes,
163164
claimNextRetryLane,
165+
includesOnlyTransitions,
164166
} from './ReactFiberLane';
165167
import {resetChildFibers} from './ReactChildFiber';
166168
import {createScopeInstance} from './ReactFiberScope';
@@ -411,7 +413,12 @@ function updateHostContainer(current: null | Fiber, workInProgress: Fiber) {
411413
portalOrRoot.pendingChildren = newChildSet;
412414
// Schedule an update on the container to swap out the container.
413415
markUpdate(workInProgress);
414-
finalizeContainerChildren(container, newChildSet);
416+
const isSuspendedTransition =
417+
renderDidSuspendWithDelay() &&
418+
includesOnlyTransitions(workInProgress.lanes);
419+
if (!isSuspendedTransition) {
420+
finalizeContainerChildren(container, newChildSet);
421+
}
415422
}
416423
}
417424
}

packages/react-reconciler/src/ReactFiberWorkLoop.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1939,6 +1939,10 @@ export function renderDidSuspendDelayIfPossible(): void {
19391939
}
19401940
}
19411941

1942+
export function renderDidSuspendWithDelay(): boolean {
1943+
return workInProgressRootExitStatus === RootSuspendedWithDelay;
1944+
}
1945+
19421946
export function renderDidError() {
19431947
if (workInProgressRootExitStatus !== RootSuspendedWithDelay) {
19441948
workInProgressRootExitStatus = RootErrored;

0 commit comments

Comments
 (0)