diff --git a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js index 69013757ae8fb..c3e83a5795400 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js @@ -10,6 +10,7 @@ 'use strict'; const React = require('react'); +const Fragment = React.Fragment; let ReactFeatureFlags = require('shared/ReactFeatureFlags'); let ReactDOM; @@ -659,4 +660,55 @@ describe('ReactDOMFiberAsync', () => { expect(formSubmitted).toBe(true); }); }); + + describe('Disable yielding', () => { + beforeEach(() => { + jest.resetModules(); + ReactFeatureFlags = require('shared/ReactFeatureFlags'); + ReactFeatureFlags.disableYielding = true; + ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false; + ReactDOM = require('react-dom'); + Scheduler = require('scheduler'); + }); + + it('wont yield during a render if yielding is disabled', () => { + class A extends React.Component { + render() { + Scheduler.yieldValue('A'); + return
{this.props.children}
; + } + } + + class B extends React.Component { + render() { + Scheduler.yieldValue('B'); + return
{this.props.children}
; + } + } + + class C extends React.Component { + render() { + Scheduler.yieldValue('C'); + return
{this.props.children}
; + } + } + + let root = ReactDOM.unstable_createRoot(container); + + root.render( + + + + + , + ); + + expect(Scheduler).toHaveYielded([]); + + Scheduler.unstable_flushNumberOfYields(2); + // Even though we just flushed two yields, we should have rendered + // everything without yielding when the flag is on. + expect(Scheduler).toHaveYielded(['A', 'B', 'C']); + }); + }); }); diff --git a/packages/react-reconciler/src/ReactFiberScheduler.js b/packages/react-reconciler/src/ReactFiberScheduler.js index fdd42867b31d3..e24e2ad714803 100644 --- a/packages/react-reconciler/src/ReactFiberScheduler.js +++ b/packages/react-reconciler/src/ReactFiberScheduler.js @@ -63,6 +63,7 @@ import { replayFailedUnitOfWorkWithInvokeGuardedCallback, warnAboutDeprecatedLifecycles, enableSuspenseServerRenderer, + disableYielding, } from 'shared/ReactFeatureFlags'; import getComponentName from 'shared/getComponentName'; import invariant from 'shared/invariant'; @@ -2019,7 +2020,7 @@ function onSuspend( msUntilTimeout: number, ): void { root.expirationTime = rootExpirationTime; - if (msUntilTimeout === 0 && !shouldYield()) { + if (msUntilTimeout === 0 && (disableYielding || !shouldYield())) { // Don't wait an additional tick. Commit the tree immediately. root.pendingCommitExpirationTime = suspendedExpirationTime; root.finishedWork = finishedWork; @@ -2233,7 +2234,11 @@ function performAsyncWork(didTimeout) { } while (root !== firstScheduledRoot); } } - performWork(NoWork, true); + let isYieldy = true; + if (disableYielding) { + isYieldy = false; + } + performWork(NoWork, isYieldy); } function performSyncWork() { diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index b9e249914b1ad..95108e7d96239 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -45,6 +45,9 @@ export function addUserTimingListener() { // Disable javascript: URL strings in href for XSS protection. export const disableJavaScriptURLs = false; +// Disables yielding during render in Concurrent Mode. Used for debugging only. +export const disableYielding = false; + // React Fire: prevent the value and checked attributes from syncing // with their related DOM properties export const disableInputAttributeSyncing = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index c1de808b851bc..64db95b8d8484 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -25,6 +25,7 @@ export const warnAboutShorthandPropertyCollision = false; export const enableSchedulerDebugging = false; export const debugRenderPhaseSideEffectsForStrictMode = true; export const disableJavaScriptURLs = false; +export const disableYielding = false; export const disableInputAttributeSyncing = false; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; export const warnAboutDeprecatedLifecycles = true; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index edfe1f5e220d0..a5cfb3d29435d 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -21,6 +21,7 @@ export const enableProfilerTimer = __PROFILE__; export const enableSchedulerTracing = __PROFILE__; export const enableSuspenseServerRenderer = false; export const disableJavaScriptURLs = false; +export const disableYielding = false; export const disableInputAttributeSyncing = false; export const enableStableConcurrentModeAPIs = false; export const warnAboutShorthandPropertyCollision = false; diff --git a/packages/shared/forks/ReactFeatureFlags.persistent.js b/packages/shared/forks/ReactFeatureFlags.persistent.js index 7b8fb48083cbb..692864b4cfd57 100644 --- a/packages/shared/forks/ReactFeatureFlags.persistent.js +++ b/packages/shared/forks/ReactFeatureFlags.persistent.js @@ -21,6 +21,7 @@ export const enableProfilerTimer = __PROFILE__; export const enableSchedulerTracing = __PROFILE__; export const enableSuspenseServerRenderer = false; export const disableJavaScriptURLs = false; +export const disableYielding = false; export const disableInputAttributeSyncing = false; export const enableStableConcurrentModeAPIs = false; export const warnAboutShorthandPropertyCollision = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 7153ac8c29903..7239941035bc5 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -21,6 +21,7 @@ export const enableProfilerTimer = false; export const enableSchedulerTracing = false; export const enableSuspenseServerRenderer = false; export const disableJavaScriptURLs = false; +export const disableYielding = false; export const disableInputAttributeSyncing = false; export const enableStableConcurrentModeAPIs = false; export const warnAboutShorthandPropertyCollision = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 660f3c19d754a..096494b839ef7 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -24,6 +24,7 @@ export const enableStableConcurrentModeAPIs = false; export const enableSchedulerDebugging = false; export const warnAboutDeprecatedSetNativeProps = false; export const disableJavaScriptURLs = false; +export const disableYielding = false; export const enableEventAPI = true; // Only used in www builds. diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index f967402adc871..e47428a52a3c3 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -17,6 +17,7 @@ export const { replayFailedUnitOfWorkWithInvokeGuardedCallback, warnAboutDeprecatedLifecycles, disableJavaScriptURLs, + disableYielding, disableInputAttributeSyncing, warnAboutShorthandPropertyCollision, warnAboutDeprecatedSetNativeProps,