From 5bb7348cc25f8cc5e1b2da0bd9fc4075576715ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Tue, 3 Dec 2019 13:38:02 -0800 Subject: [PATCH] Don't group Idle/Offscreen work with other work (#17456) When we suspend we always try a lower level but we shouldn't try offscreen. --- .../src/ReactFiberWorkLoop.js | 19 ++- ...tSuspenseWithNoopRenderer-test.internal.js | 115 +++++++++++++++++- packages/shared/ReactFeatureFlags.js | 2 + .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.persistent.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../shared/forks/ReactFeatureFlags.www.js | 1 + 9 files changed, 137 insertions(+), 5 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index ad6ff4037edf1..8bf99ce1139bf 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -25,6 +25,7 @@ import { warnAboutUnmockedScheduler, flushSuspenseFallbacksInTests, disableSchedulerTimeoutBasedOnReactExpirationTime, + enableTrainModelFix, } from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import invariant from 'shared/invariant'; @@ -539,9 +540,19 @@ function getNextRootExpirationTimeToWorkOn(root: FiberRoot): ExpirationTime { // on whichever is higher priority. const lastPingedTime = root.lastPingedTime; const nextKnownPendingLevel = root.nextKnownPendingLevel; - return lastPingedTime > nextKnownPendingLevel - ? lastPingedTime - : nextKnownPendingLevel; + const nextLevel = + lastPingedTime > nextKnownPendingLevel + ? lastPingedTime + : nextKnownPendingLevel; + if ( + enableTrainModelFix && + nextLevel <= Idle && + firstPendingTime !== nextLevel + ) { + // Don't work on Idle/Never priority unless everything else is committed. + return NoWork; + } + return nextLevel; } // Use this function to schedule a task for a root. There's only one task per @@ -2362,7 +2373,7 @@ export function pingSuspendedRoot( // Mark the time at which this ping was scheduled. root.lastPingedTime = suspendedTime; - if (root.finishedExpirationTime === suspendedTime) { + if (!enableTrainModelFix && root.finishedExpirationTime === suspendedTime) { // If there's a pending fallback waiting to commit, throw it away. root.finishedExpirationTime = NoWork; root.finishedWork = null; diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js index 86fb78244188d..97f247895d553 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js @@ -2222,7 +2222,8 @@ describe('ReactSuspenseWithNoopRenderer', () => { Scheduler.unstable_runWithPriority(Scheduler.unstable_IdlePriority, () => ReactNoop.render(), ); - expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading A...']); + // We won't even work on Idle priority. + expect(Scheduler).toFlushAndYield([]); // We're still suspended. expect(ReactNoop.getChildren()).toEqual([]); @@ -2789,4 +2790,116 @@ describe('ReactSuspenseWithNoopRenderer', () => { expect(root).toMatchRenderedOutput(); }); + + it('should not render hidden content while suspended on higher pri', async () => { + function Offscreen() { + Scheduler.unstable_yieldValue('Offscreen'); + return 'Offscreen'; + } + function App({showContent}) { + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('Commit'); + }); + return ( + <> + + }> + {showContent ? : null} + + + ); + } + + // Initial render. + ReactNoop.render(); + expect(Scheduler).toFlushAndYieldThrough(['Commit']); + expect(ReactNoop).toMatchRenderedOutput(