Revert #14756 changes to ReactFiberScheduler (#14992)
* Revert #14756 changes to ReactFiberScheduler This PR introduced some bugs in concurrent mode during internal testing. Until we figure out a proper solution, I'm going to try reverting it. Not totally certain this is sufficient to unbreak the bugs we found, but I'm using this branch to determine that. * Add back commented out Scheduler import With a note not to use named imports next time we import Scheduler in this module.
Andrew Clark committed
Mar 1, 2019 at 13:10 UTC
9d756d903fdd07895fc320962c3fab95fa099bd5
1 file changed
+81
-72
packages/react-reconciler/src/ReactFiberScheduler.js
+81
-72
@@ -12,12 +12,16 @@ import type {Batch, FiberRoot} from './ReactFiberRoot';
12
import type {ExpirationTime} from './ReactFiberExpirationTime';
13
import type {Interaction} from 'scheduler/src/Tracing';
14
15
+// Intentionally not named imports because Rollup would use dynamic dispatch for
16
+// CommonJS interop named imports.
17
+// TODO: We're not using this import anymore, but I've left this here so we
18
+// don't accidentally use named imports when we add it back.
19
+// import * as Scheduler from 'scheduler';
20
import {
21
__interactionsRef,
22
__subscriberRef,
23
unstable_wrap as Scheduler_tracing_wrap,
24
} from 'scheduler/tracing';
20
-import * as Scheduler from 'scheduler';
25
import {
26
invokeGuardedCallback,
27
hasCaughtError,
@@ -126,7 +130,7 @@ import {
130
computeAsyncExpiration,
131
computeInteractiveExpiration,
132
} from './ReactFiberExpirationTime';
129
-import {ConcurrentMode, ProfileMode, NoContext} from './ReactTypeOfMode';
133
+import {ConcurrentMode, ProfileMode} from './ReactTypeOfMode';
134
import {enqueueUpdate, resetCurrentlyProcessingQueue} from './ReactUpdateQueue';
135
import {createCapturedValue} from './ReactCapturedValue';
136
import {
@@ -172,19 +176,6 @@ export type Thenable = {
176
then(resolve: () => mixed, reject?: () => mixed): mixed,
177
};
178
175
-// Intentionally not named imports because Rollup would
176
-// use dynamic dispatch for CommonJS interop named imports.
177
-const {
178
- unstable_next: Scheduler_next,
179
- unstable_getCurrentPriorityLevel: getCurrentPriorityLevel,
180
- unstable_runWithPriority: runWithPriority,
181
- unstable_ImmediatePriority: ImmediatePriority,
182
- unstable_UserBlockingPriority: UserBlockingPriority,
183
- unstable_NormalPriority: NormalPriority,
184
- unstable_LowPriority: LowPriority,
185
- unstable_IdlePriority: IdlePriority,
186
-} = Scheduler;
187
-
179
const {ReactCurrentDispatcher, ReactCurrentOwner} = ReactSharedInternals;
180
181
let didWarnAboutStateTransition;
@@ -259,6 +250,11 @@ if (__DEV__) {
250
// Used to ensure computeUniqueAsyncExpiration is monotonically decreasing.
251
let lastUniqueAsyncExpiration: number = Sync - 1;
252
253
+// Represents the expiration time that incoming updates should use. (If this
254
+// is NoWork, use the default strategy: async updates in async mode, sync
255
+// updates in sync mode.)
256
+let expirationContext: ExpirationTime = NoWork;
257
+
258
let isWorking: boolean = false;
259
260
// The next work in progress fiber that we're currently working on.
@@ -811,9 +807,7 @@ function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
807
// here because that code is still in flux.
808
callback = Scheduler_tracing_wrap(callback);
809
}
814
- passiveEffectCallbackHandle = runWithPriority(NormalPriority, () => {
815
- return schedulePassiveEffects(callback);
816
- });
810
+ passiveEffectCallbackHandle = schedulePassiveEffects(callback);
811
passiveEffectCallback = callback;
812
}
813
@@ -1597,58 +1591,52 @@ function computeUniqueAsyncExpiration(): ExpirationTime {
1591
}
1592
1593
function computeExpirationForFiber(currentTime: ExpirationTime, fiber: Fiber) {
1600
- const priorityLevel = getCurrentPriorityLevel();
1601
-
1594
let expirationTime;
1603
- if ((fiber.mode & ConcurrentMode) === NoContext) {
1604
- // Outside of concurrent mode, updates are always synchronous.
1605
- expirationTime = Sync;
1606
- } else if (isWorking && !isCommitting) {
1607
- // During render phase, updates expire during as the current render.
1608
- expirationTime = nextRenderExpirationTime;
1595
+ if (expirationContext !== NoWork) {
1596
+ // An explicit expiration context was set;
1597
+ expirationTime = expirationContext;
1598
+ } else if (isWorking) {
1599
+ if (isCommitting) {
1600
+ // Updates that occur during the commit phase should have sync priority
1601
+ // by default.
1602
+ expirationTime = Sync;
1603
+ } else {
1604
+ // Updates during the render phase should expire at the same time as
1605
+ // the work that is being rendered.
1606
+ expirationTime = nextRenderExpirationTime;
1607
+ }
1608
} else {
1610
- switch (priorityLevel) {
1611
- case ImmediatePriority:
1612
- expirationTime = Sync;
1613
- break;
1614
- case UserBlockingPriority:
1609
+ // No explicit expiration context was set, and we're not currently
1610
+ // performing work. Calculate a new expiration time.
1611
+ if (fiber.mode & ConcurrentMode) {
1612
+ if (isBatchingInteractiveUpdates) {
1613
+ // This is an interactive update
1614
expirationTime = computeInteractiveExpiration(currentTime);
1616
- break;
1617
- case NormalPriority:
1618
- // This is a normal, concurrent update
1615
+ } else {
1616
+ // This is an async update
1617
expirationTime = computeAsyncExpiration(currentTime);
1620
- break;
1621
- case LowPriority:
1622
- case IdlePriority:
1623
- expirationTime = Never;
1624
- break;
1625
- default:
1626
- invariant(
1627
- false,
1628
- 'Unknown priority level. This error is likely caused by a bug in ' +
1629
- 'React. Please file an issue.',
1630
- );
1631
- }
1632
-
1633
- // If we're in the middle of rendering a tree, do not update at the same
1634
- // expiration time that is already rendering.
1635
- if (nextRoot !== null && expirationTime === nextRenderExpirationTime) {
1636
- expirationTime -= 1;
1618
+ }
1619
+ // If we're in the middle of rendering a tree, do not update at the same
1620
+ // expiration time that is already rendering.
1621
+ if (nextRoot !== null && expirationTime === nextRenderExpirationTime) {
1622
+ expirationTime -= 1;
1623
+ }
1624
+ } else {
1625
+ // This is a sync update
1626
+ expirationTime = Sync;
1627
}
1628
}
1639
-
1640
- // Keep track of the lowest pending interactive expiration time. This
1641
- // allows us to synchronously flush all interactive updates
1642
- // when needed.
1643
- // TODO: Move this to renderer?
1644
- if (
1645
- priorityLevel === UserBlockingPriority &&
1646
- (lowestPriorityPendingInteractiveExpirationTime === NoWork ||
1647
- expirationTime < lowestPriorityPendingInteractiveExpirationTime)
1648
- ) {
1649
- lowestPriorityPendingInteractiveExpirationTime = expirationTime;
1629
+ if (isBatchingInteractiveUpdates) {
1630
+ // This is an interactive update. Keep track of the lowest pending
1631
+ // interactive expiration time. This allows us to synchronously flush
1632
+ // all interactive updates when needed.
1633
+ if (
1634
+ lowestPriorityPendingInteractiveExpirationTime === NoWork ||
1635
+ expirationTime < lowestPriorityPendingInteractiveExpirationTime
1636
+ ) {
1637
+ lowestPriorityPendingInteractiveExpirationTime = expirationTime;
1638
+ }
1639
}
1651
-
1640
return expirationTime;
1641
}
1642
@@ -1909,6 +1897,20 @@ function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
1897
}
1898
}
1899
1900
+function deferredUpdates<A>(fn: () => A): A {
1901
+ const currentTime = requestCurrentTime();
1902
+ const previousExpirationContext = expirationContext;
1903
+ const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates;
1904
+ expirationContext = computeAsyncExpiration(currentTime);
1905
+ isBatchingInteractiveUpdates = false;
1906
+ try {
1907
+ return fn();
1908
+ } finally {
1909
+ expirationContext = previousExpirationContext;
1910
+ isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates;
1911
+ }
1912
+}
1913
+
1914
function syncUpdates<A, B, C0, D, R>(
1915
fn: (A, B, C0, D) => R,
1916
a: A,
@@ -1916,9 +1918,13 @@ function syncUpdates<A, B, C0, D, R>(
1918
c: C0,
1919
d: D,
1920
): R {
1919
- return runWithPriority(ImmediatePriority, () => {
1921
+ const previousExpirationContext = expirationContext;
1922
+ expirationContext = Sync;
1923
+ try {
1924
return fn(a, b, c, d);
1921
- });
1925
+ } finally {
1926
+ expirationContext = previousExpirationContext;
1927
+ }
1928
}
1929
1930
// TODO: Everything below this is written as if it has been lifted to the
@@ -1939,6 +1945,7 @@ let unhandledError: mixed | null = null;
1945
1946
let isBatchingUpdates: boolean = false;
1947
let isUnbatchingUpdates: boolean = false;
1948
+let isBatchingInteractiveUpdates: boolean = false;
1949
1950
let completedBatches: Array<Batch> | null = null;
1951
@@ -2450,9 +2457,7 @@ function completeRoot(
2457
lastCommittedRootDuringThisBatch = root;
2458
nestedUpdateCount = 0;
2459
}
2453
- runWithPriority(ImmediatePriority, () => {
2454
- commitRoot(root, finishedWork);
2455
- });
2460
+ commitRoot(root, finishedWork);
2461
}
2462
2463
function onUncaughtError(error: mixed) {
@@ -2518,6 +2523,9 @@ function flushSync<A, R>(fn: (a: A) => R, a: A): R {
2523
}
2524
2525
function interactiveUpdates<A, B, R>(fn: (A, B) => R, a: A, b: B): R {
2526
+ if (isBatchingInteractiveUpdates) {
2527
+ return fn(a, b);
2528
+ }
2529
// If there are any pending interactive updates, synchronously flush them.
2530
// This needs to happen before we read any handlers, because the effect of
2531
// the previous event may influence which handlers are called during
@@ -2531,13 +2539,14 @@ function interactiveUpdates<A, B, R>(fn: (A, B) => R, a: A, b: B): R {
2539
performWork(lowestPriorityPendingInteractiveExpirationTime, false);
2540
lowestPriorityPendingInteractiveExpirationTime = NoWork;
2541
}
2542
+ const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates;
2543
const previousIsBatchingUpdates = isBatchingUpdates;
2544
+ isBatchingInteractiveUpdates = true;
2545
isBatchingUpdates = true;
2546
try {
2537
- return runWithPriority(UserBlockingPriority, () => {
2538
- return fn(a, b);
2539
- });
2547
+ return fn(a, b);
2548
} finally {
2549
+ isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates;
2550
isBatchingUpdates = previousIsBatchingUpdates;
2551
if (!isBatchingUpdates && !isRendering) {
2552
performSyncWork();
@@ -2588,7 +2597,7 @@ export {
2597
unbatchedUpdates,
2598
flushSync,
2599
flushControlled,
2591
- Scheduler_next as deferredUpdates,
2600
+ deferredUpdates,
2601
syncUpdates,
2602
interactiveUpdates,
2603
flushInteractiveUpdates,