Skip to content

Commit 0bd3dc3

Browse files
committed
Add flag to disable behavior change
1 parent 267bee9 commit 0bd3dc3

12 files changed

+62
-6
lines changed

packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
10671067
assertLog(['Inner Mouse Enter', 'Outer Mouse Enter']);
10681068
});
10691069

1070+
// @gate enableDeferRootSchedulingToMicrotask
10701071
it('Outer hydrates first then Inner', async () => {
10711072
dispatchMouseHoverEvent(innerDiv);
10721073

packages/react-reconciler/src/ReactFiberRootScheduler.js

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {FiberRoot} from './ReactInternalTypes';
1111
import type {Lane} from './ReactFiberLane';
1212
import type {PriorityLevel} from 'scheduler/src/SchedulerPriorities';
1313

14+
import {enableDeferRootSchedulingToMicrotask} from 'shared/ReactFeatureFlags';
1415
import {
1516
NoLane,
1617
NoLanes,
@@ -41,6 +42,7 @@ import {
4142
cancelCallback as Scheduler_cancelCallback,
4243
scheduleCallback as Scheduler_scheduleCallback,
4344
now,
45+
ImmediatePriority,
4446
} from './Scheduler';
4547
import {
4648
DiscreteEventPriority,
@@ -111,14 +113,30 @@ export function ensureRootIsScheduled(root: FiberRoot): void {
111113
scheduleImmediateTask(processRootScheduleInMicrotask);
112114
}
113115
}
116+
117+
if (!enableDeferRootSchedulingToMicrotask) {
118+
// While this flag is disabled, we schedule the task immediately instead
119+
// of waiting for the microtask to fire.
120+
// TODO: We need to land enableDeferRootSchedulingToMicrotask ASAP to
121+
// unblock additional features we have planned.
122+
scheduleTaskForRootDuringMicrotask(root, now());
123+
if (mightHavePendingSyncWork) {
124+
// Before enableDeferRootSchedulingToMicrotask, we used to flush
125+
// synchronous work in a macrotask instead of a microtask. (Except for
126+
// discrete events or flushSync, which will force this to run
127+
// even sooner.)
128+
scheduleCallback(ImmediatePriority, flushSyncCallbacks);
129+
}
130+
}
114131
}
115132

116133
// TODO: Rename to something else. This isn't a generic callback queue anymore.
117134
// I only kept the existing name to reduce noise in the initial PR diff.
118-
export function flushSyncCallbacks() {
135+
export function flushSyncCallbacks(): null {
119136
// This is allowed to be called synchronously, but the caller should check
120137
// the execution context first.
121138
flushSyncWorkAcrossRoots_impl(false);
139+
return null;
122140
}
123141

124142
// TODO: Rename to something else. This isn't a generic callback queue anymore.
@@ -254,9 +272,16 @@ function processRootScheduleInMicrotask() {
254272
root = next;
255273
}
256274

257-
// At the end of the microtask, flush any pending synchronous work. This has
258-
// to come at the end, because it does actual rendering work that might throw.
259-
flushSyncCallbacks();
275+
if (enableDeferRootSchedulingToMicrotask) {
276+
// At the end of the microtask, flush any pending synchronous work. This has
277+
// to come at the end, because it does actual rendering work that might throw.
278+
flushSyncCallbacks();
279+
} else {
280+
// Before enableDeferRootSchedulingToMicrotask, we used to flush synchronous
281+
// work in a macrotask instead of a microtask. (Except for discrete events
282+
// or flushSync, which will force this to run even sooner.)
283+
scheduleCallback(ImmediatePriority, flushSyncCallbacks);
284+
}
260285
}
261286

262287
function scheduleTaskForRootDuringMicrotask(
@@ -267,6 +292,9 @@ function scheduleTaskForRootDuringMicrotask(
267292
// rendering task right before we yield to the main thread. It should never be
268293
// called synchronously.
269294
//
295+
// TODO: Unless enableDeferRootSchedulingToMicrotask is off. We need to land
296+
// that ASAP to unblock additional features we have planned.
297+
//
270298
// This function also never performs React work synchronously; it should
271299
// only schedule work to be performed later, in a separate task or microtask.
272300

packages/react/src/__tests__/ReactProfiler-test.internal.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,22 @@ describe(`onRender`, () => {
258258

259259
// TODO: unstable_now is called by more places than just the profiler.
260260
// Rewrite this test so it's less fragile.
261-
assertLog(['read current time', 'read current time', 'read current time']);
261+
if (gate(flags => flags.enableDeferRootSchedulingToMicrotask)) {
262+
assertLog([
263+
'read current time',
264+
'read current time',
265+
'read current time',
266+
]);
267+
} else {
268+
assertLog([
269+
'read current time',
270+
'read current time',
271+
'read current time',
272+
'read current time',
273+
'read current time',
274+
'read current time',
275+
]);
276+
}
262277

263278
// Restore original mock
264279
jest.mock('scheduler', () => jest.requireActual('scheduler/unstable_mock'));

packages/shared/ReactFeatureFlags.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ export const enableSchedulerDebugging = false;
5252
// Need to remove didTimeout argument from Scheduler before landing
5353
export const disableSchedulerTimeoutInWorkLoop = false;
5454

55+
// This will break some internal tests at Meta so we need to gate this until
56+
// those can be fixed.
57+
export const enableDeferRootSchedulingToMicrotask = true;
58+
5559
// -----------------------------------------------------------------------------
5660
// Slated for removal in the future (significant effort)
5761
//

packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import typeof * as DynamicFlagsType from 'ReactNativeInternalFeatureFlags';
2121
// update the test configuration.
2222

2323
export const enableUseRefAccessWarning = __VARIANT__;
24+
export const enableDeferRootSchedulingToMicrotask = __VARIANT__;
2425

2526
// Flow magic to verify the exports of this file match the original version.
2627
((((null: any): ExportsType): DynamicFlagsType): ExportsType);

packages/shared/forks/ReactFeatureFlags.native-fb.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import * as dynamicFlags from 'ReactNativeInternalFeatureFlags';
1717

1818
// We destructure each value before re-exporting to avoid a dynamic look-up on
1919
// the exports object every time a flag is read.
20-
export const {enableUseRefAccessWarning} = dynamicFlags;
20+
export const {enableUseRefAccessWarning, enableDeferRootSchedulingToMicrotask} =
21+
dynamicFlags;
2122

2223
// The rest of the flags are static for better dead code elimination.
2324
export const enableDebugTracing = false;

packages/shared/forks/ReactFeatureFlags.native-oss.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export const enableHostSingletons = true;
7070

7171
export const useModernStrictMode = false;
7272
export const enableFizzExternalRuntime = false;
73+
export const enableDeferRootSchedulingToMicrotask = true;
7374

7475
// Flow magic to verify the exports of this file match the original version.
7576
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

packages/shared/forks/ReactFeatureFlags.test-renderer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export const enableHostSingletons = true;
7070

7171
export const useModernStrictMode = false;
7272
export const enableFizzExternalRuntime = false;
73+
export const enableDeferRootSchedulingToMicrotask = true;
7374

7475
// Flow magic to verify the exports of this file match the original version.
7576
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

packages/shared/forks/ReactFeatureFlags.test-renderer.www.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export const enableHostSingletons = true;
7272

7373
export const useModernStrictMode = false;
7474
export const enableFizzExternalRuntime = false;
75+
export const enableDeferRootSchedulingToMicrotask = true;
7576

7677
// Flow magic to verify the exports of this file match the original version.
7778
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

packages/shared/forks/ReactFeatureFlags.www-dynamic.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const enableLazyContextPropagation = __VARIANT__;
2424
export const enableUnifiedSyncLane = __VARIANT__;
2525
export const enableTransitionTracing = __VARIANT__;
2626
export const enableCustomElementPropertySupport = __VARIANT__;
27+
export const enableDeferRootSchedulingToMicrotask = __VARIANT__;
2728

2829
// Enable this flag to help with concurrent mode debugging.
2930
// It logs information to the console about React scheduling, rendering, and commit phases.

packages/shared/forks/ReactFeatureFlags.www.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export const {
2828
enableUnifiedSyncLane,
2929
enableTransitionTracing,
3030
enableCustomElementPropertySupport,
31+
enableDeferRootSchedulingToMicrotask,
3132
} = dynamicFeatureFlags;
3233

3334
// On WWW, __EXPERIMENTAL__ is used for a new modern build.

scripts/flow/xplat.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99

1010
declare module 'ReactNativeInternalFeatureFlags' {
1111
declare export var enableUseRefAccessWarning: boolean;
12+
declare export var enableDeferRootSchedulingToMicrotask: boolean;
1213
}

0 commit comments

Comments
 (0)