Skip to content

Commit

Permalink
fix suspense throttling
Browse files Browse the repository at this point in the history
  • Loading branch information
sunderls committed Apr 4, 2022
1 parent b8cfda1 commit a2bfae1
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 16 deletions.
18 changes: 10 additions & 8 deletions packages/react-reconciler/src/ReactFiberCommitWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -2250,22 +2250,25 @@ function commitMutationEffectsOnFiber(
}
}

if (flags & Visibility) {
switch (finishedWork.tag) {
case SuspenseComponent: {
const newState: OffscreenState | null = finishedWork.memoizedState;
switch (finishedWork.tag) {
case SuspenseComponent: {
const offscreenFiber: Fiber = (finishedWork.child: any);
if (offscreenFiber.flags & Visibility) {
const newState: OffscreenState | null = offscreenFiber.memoizedState;
const isHidden = newState !== null;
if (isHidden) {
const current = finishedWork.alternate;
const current = offscreenFiber.alternate;
const wasHidden = current !== null && current.memoizedState !== null;
if (!wasHidden) {
// TODO: Move to passive phase
markCommitTimeOfFallback();
}
}
break;
}
case OffscreenComponent: {
break;
}
case OffscreenComponent: {
if (flags & Visibility) {
const newState: OffscreenState | null = finishedWork.memoizedState;
const isHidden = newState !== null;
const current = finishedWork.alternate;
Expand Down Expand Up @@ -2301,7 +2304,6 @@ function commitMutationEffectsOnFiber(
}
}
}

// The following switch statement is only concerned about placement,
// updates, and deletions. To avoid needing to add a case for every possible
// bitmap value, we remove the secondary effects from the effect tag and
Expand Down
18 changes: 10 additions & 8 deletions packages/react-reconciler/src/ReactFiberCommitWork.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -2250,22 +2250,25 @@ function commitMutationEffectsOnFiber(
}
}

if (flags & Visibility) {
switch (finishedWork.tag) {
case SuspenseComponent: {
const newState: OffscreenState | null = finishedWork.memoizedState;
switch (finishedWork.tag) {
case SuspenseComponent: {
const offscreenFiber: Fiber = (finishedWork.child: any);
if (offscreenFiber.flags & Visibility) {
const newState: OffscreenState | null = offscreenFiber.memoizedState;
const isHidden = newState !== null;
if (isHidden) {
const current = finishedWork.alternate;
const current = offscreenFiber.alternate;
const wasHidden = current !== null && current.memoizedState !== null;
if (!wasHidden) {
// TODO: Move to passive phase
markCommitTimeOfFallback();
}
}
break;
}
case OffscreenComponent: {
break;
}
case OffscreenComponent: {
if (flags & Visibility) {
const newState: OffscreenState | null = finishedWork.memoizedState;
const isHidden = newState !== null;
const current = finishedWork.alternate;
Expand Down Expand Up @@ -2301,7 +2304,6 @@ function commitMutationEffectsOnFiber(
}
}
}

// The following switch statement is only concerned about placement,
// updates, and deletions. To avoid needing to add a case for every possible
// bitmap value, we remove the secondary effects from the effect tag and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,57 @@ describe('ReactSuspense', () => {
expect(root).toMatchRenderedOutput('AsyncAfter SuspenseSibling');
});

it('globally throttles fallback committing', () => {
function Foo() {
Scheduler.unstable_yieldValue('Foo');
return (
<Suspense fallback={<Text text="Loading..." />}>
<AsyncText text="A" ms={200} />
<Suspense fallback={<Text text="Loading more..." />}>
<AsyncText text="B" ms={300} />
</Suspense>
</Suspense>
);
}

// throttling works on fallback committing
// here advance some time to skip the first threshold
jest.advanceTimersByTime(600);
Scheduler.unstable_advanceTime(600);

const root = ReactTestRenderer.create(<Foo />, {
unstable_isConcurrent: true,
});

expect(Scheduler).toFlushAndYield([
'Foo',
'Suspend! [A]',
'Suspend! [B]',
'Loading more...',
'Loading...',
]);
expect(root).toMatchRenderedOutput('Loading...');

// resolve A
jest.advanceTimersByTime(200);
Scheduler.unstable_advanceTime(200);
expect(Scheduler).toHaveYielded(['Promise resolved [A]']);
expect(Scheduler).toFlushAndYield(['A', 'Suspend! [B]', 'Loading more...']);

// should still renders previous fallback
expect(root).toMatchRenderedOutput('Loading...');

// resolve B
jest.advanceTimersByTime(100);
Scheduler.unstable_advanceTime(100);
expect(Scheduler).toHaveYielded(['Promise resolved [B]']);

// before commiting we still shows previous fallback
expect(root).toMatchRenderedOutput('Loading...');
expect(Scheduler).toFlushAndYield(['A', 'B']);
expect(root).toMatchRenderedOutput('AB');
});

// @gate !enableSyncDefaultUpdates
it(
'interrupts current render when something suspends with a ' +
Expand Down

0 comments on commit a2bfae1

Please sign in to comment.