Clean up timeoutMs-related implementation details (#19704)
* Disable busyDelayMs and busyMinDurationMs Refer to explanation in previous commit. * Remove unnecessary work loop variables Since we no longer support SuspenseConfig options, we don't need to track these values. * Remove unnecessary Update fields
Andrew Clark committed
Aug 27, 2020 at 11:47 UTC
1eaafc9ade46ba708b2361b324dd907d019e3939
17 files changed
+25
-411
packages/react-reconciler/src/ReactFiberClassComponent.new.js
+3
-3
@@ -199,7 +199,7 @@ const classComponentUpdater = {
199
const suspenseConfig = requestCurrentSuspenseConfig();
200
const lane = requestUpdateLane(fiber, suspenseConfig);
201
202
- const update = createUpdate(eventTime, lane, suspenseConfig);
202
+ const update = createUpdate(eventTime, lane);
203
update.payload = payload;
204
if (callback !== undefined && callback !== null) {
205
if (__DEV__) {
@@ -230,7 +230,7 @@ const classComponentUpdater = {
230
const suspenseConfig = requestCurrentSuspenseConfig();
231
const lane = requestUpdateLane(fiber, suspenseConfig);
232
233
- const update = createUpdate(eventTime, lane, suspenseConfig);
233
+ const update = createUpdate(eventTime, lane);
234
update.tag = ReplaceState;
235
update.payload = payload;
236
@@ -263,7 +263,7 @@ const classComponentUpdater = {
263
const suspenseConfig = requestCurrentSuspenseConfig();
264
const lane = requestUpdateLane(fiber, suspenseConfig);
265
266
- const update = createUpdate(eventTime, lane, suspenseConfig);
266
+ const update = createUpdate(eventTime, lane);
267
update.tag = ForceUpdate;
268
269
if (callback !== undefined && callback !== null) {
packages/react-reconciler/src/ReactFiberClassComponent.old.js
+3
-3
@@ -199,7 +199,7 @@ const classComponentUpdater = {
199
const suspenseConfig = requestCurrentSuspenseConfig();
200
const lane = requestUpdateLane(fiber, suspenseConfig);
201
202
- const update = createUpdate(eventTime, lane, suspenseConfig);
202
+ const update = createUpdate(eventTime, lane);
203
update.payload = payload;
204
if (callback !== undefined && callback !== null) {
205
if (__DEV__) {
@@ -230,7 +230,7 @@ const classComponentUpdater = {
230
const suspenseConfig = requestCurrentSuspenseConfig();
231
const lane = requestUpdateLane(fiber, suspenseConfig);
232
233
- const update = createUpdate(eventTime, lane, suspenseConfig);
233
+ const update = createUpdate(eventTime, lane);
234
update.tag = ReplaceState;
235
update.payload = payload;
236
@@ -263,7 +263,7 @@ const classComponentUpdater = {
263
const suspenseConfig = requestCurrentSuspenseConfig();
264
const lane = requestUpdateLane(fiber, suspenseConfig);
265
266
- const update = createUpdate(eventTime, lane, suspenseConfig);
266
+ const update = createUpdate(eventTime, lane);
267
update.tag = ForceUpdate;
268
269
if (callback !== undefined && callback !== null) {
packages/react-reconciler/src/ReactFiberHooks.new.js
-21
@@ -63,7 +63,6 @@ import {
63
warnIfNotCurrentlyActingEffectsInDEV,
64
warnIfNotCurrentlyActingUpdatesInDev,
65
warnIfNotScopedWithMatchingAct,
66
- markRenderEventTimeAndConfig,
66
markSkippedUpdateLanes,
67
} from './ReactFiberWorkLoop.new';
68
@@ -97,11 +96,7 @@ import {markStateUpdateScheduled} from './SchedulingProfiler';
96
const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
97
98
type Update<S, A> = {|
100
- // TODO: Temporary field. Will remove this by storing a map of
101
- // transition -> start time on the root.
102
- eventTime: number,
99
lane: Lane,
104
- suspenseConfig: null | SuspenseConfig,
100
action: A,
101
eagerReducer: ((S, A) => S) | null,
102
eagerState: S | null,
@@ -715,17 +710,13 @@ function updateReducer<S, I, A>(
710
let newBaseQueueLast = null;
711
let update = first;
712
do {
718
- const suspenseConfig = update.suspenseConfig;
713
const updateLane = update.lane;
720
- const updateEventTime = update.eventTime;
714
if (!isSubsetOfLanes(renderLanes, updateLane)) {
715
// Priority is insufficient. Skip this update. If this is the first
716
// skipped update, the previous update/state is the new base
717
// update/state.
718
const clone: Update<S, A> = {
726
- eventTime: updateEventTime,
719
lane: updateLane,
728
- suspenseConfig: suspenseConfig,
720
action: update.action,
721
eagerReducer: update.eagerReducer,
722
eagerState: update.eagerState,
@@ -750,12 +741,10 @@ function updateReducer<S, I, A>(
741
742
if (newBaseQueueLast !== null) {
743
const clone: Update<S, A> = {
753
- eventTime: updateEventTime,
744
// This update is going to be committed so we never want uncommit
745
// it. Using NoLane works because 0 is a subset of all bitmasks, so
746
// this will never be skipped by the check above.
747
lane: NoLane,
758
- suspenseConfig: update.suspenseConfig,
748
action: update.action,
749
eagerReducer: update.eagerReducer,
750
eagerState: update.eagerState,
@@ -764,14 +753,6 @@ function updateReducer<S, I, A>(
753
newBaseQueueLast = newBaseQueueLast.next = clone;
754
}
755
767
- // Mark the event time of this update as relevant to this render pass.
768
- // TODO: This should ideally use the true event time of this update rather than
769
- // its priority which is a derived and not reverseable value.
770
- // TODO: We should skip this update if it was already committed but currently
771
- // we have no way of detecting the difference between a committed and suspended
772
- // update here.
773
- markRenderEventTimeAndConfig(updateEventTime, suspenseConfig);
774
-
756
// Process this update.
757
if (update.eagerReducer === reducer) {
758
// If this update was processed eagerly, and its reducer matches the
@@ -1708,9 +1689,7 @@ function dispatchAction<S, A>(
1689
const lane = requestUpdateLane(fiber, suspenseConfig);
1690
1691
const update: Update<S, A> = {
1711
- eventTime,
1692
lane,
1713
- suspenseConfig,
1693
action,
1694
eagerReducer: null,
1695
eagerState: null,
packages/react-reconciler/src/ReactFiberHooks.old.js
-21
@@ -62,7 +62,6 @@ import {
62
warnIfNotCurrentlyActingEffectsInDEV,
63
warnIfNotCurrentlyActingUpdatesInDev,
64
warnIfNotScopedWithMatchingAct,
65
- markRenderEventTimeAndConfig,
65
markSkippedUpdateLanes,
66
} from './ReactFiberWorkLoop.old';
67
@@ -96,11 +95,7 @@ import {markStateUpdateScheduled} from './SchedulingProfiler';
95
const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
96
97
type Update<S, A> = {|
99
- // TODO: Temporary field. Will remove this by storing a map of
100
- // transition -> start time on the root.
101
- eventTime: number,
98
lane: Lane,
103
- suspenseConfig: null | SuspenseConfig,
99
action: A,
100
eagerReducer: ((S, A) => S) | null,
101
eagerState: S | null,
@@ -714,17 +709,13 @@ function updateReducer<S, I, A>(
709
let newBaseQueueLast = null;
710
let update = first;
711
do {
717
- const suspenseConfig = update.suspenseConfig;
712
const updateLane = update.lane;
719
- const updateEventTime = update.eventTime;
713
if (!isSubsetOfLanes(renderLanes, updateLane)) {
714
// Priority is insufficient. Skip this update. If this is the first
715
// skipped update, the previous update/state is the new base
716
// update/state.
717
const clone: Update<S, A> = {
725
- eventTime: updateEventTime,
718
lane: updateLane,
727
- suspenseConfig: suspenseConfig,
719
action: update.action,
720
eagerReducer: update.eagerReducer,
721
eagerState: update.eagerState,
@@ -749,12 +740,10 @@ function updateReducer<S, I, A>(
740
741
if (newBaseQueueLast !== null) {
742
const clone: Update<S, A> = {
752
- eventTime: updateEventTime,
743
// This update is going to be committed so we never want uncommit
744
// it. Using NoLane works because 0 is a subset of all bitmasks, so
745
// this will never be skipped by the check above.
746
lane: NoLane,
757
- suspenseConfig: update.suspenseConfig,
747
action: update.action,
748
eagerReducer: update.eagerReducer,
749
eagerState: update.eagerState,
@@ -763,14 +752,6 @@ function updateReducer<S, I, A>(
752
newBaseQueueLast = newBaseQueueLast.next = clone;
753
}
754
766
- // Mark the event time of this update as relevant to this render pass.
767
- // TODO: This should ideally use the true event time of this update rather than
768
- // its priority which is a derived and not reversible value.
769
- // TODO: We should skip this update if it was already committed but currently
770
- // we have no way of detecting the difference between a committed and suspended
771
- // update here.
772
- markRenderEventTimeAndConfig(updateEventTime, suspenseConfig);
773
-
755
// Process this update.
756
if (update.eagerReducer === reducer) {
757
// If this update was processed eagerly, and its reducer matches the
@@ -1706,9 +1687,7 @@ function dispatchAction<S, A>(
1687
const lane = requestUpdateLane(fiber, suspenseConfig);
1688
1689
const update: Update<S, A> = {
1709
- eventTime,
1690
lane,
1711
- suspenseConfig,
1691
action,
1692
eagerReducer: null,
1693
eagerState: null,
packages/react-reconciler/src/ReactFiberLane.js
+3
@@ -461,6 +461,9 @@ export function includesNonIdleWork(lanes: Lanes) {
461
export function includesOnlyRetries(lanes: Lanes) {
462
return (lanes & RetryLanes) === lanes;
463
}
464
+export function includesOnlyTransitions(lanes: Lanes) {
465
+ return (lanes & TransitionLanes) === lanes;
466
+}
467
468
// To ensure consistency across multiple updates in the same event, this should
469
// be a pure function, so that it always returns the same lane for given inputs.
packages/react-reconciler/src/ReactFiberNewContext.new.js
-1
@@ -212,7 +212,6 @@ export function propagateContextChange(
212
const update = createUpdate(
213
NoTimestamp,
214
pickArbitraryLane(renderLanes),
215
- null,
215
);
216
update.tag = ForceUpdate;
217
// TODO: Because we don't have a work-in-progress, this will add the
packages/react-reconciler/src/ReactFiberNewContext.old.js
-1
@@ -212,7 +212,6 @@ export function propagateContextChange(
212
const update = createUpdate(
213
NoTimestamp,
214
pickArbitraryLane(renderLanes),
215
- null,
215
);
216
update.tag = ForceUpdate;
217
// TODO: Because we don't have a work-in-progress, this will add the
packages/react-reconciler/src/ReactFiberReconciler.new.js
+1
-1
@@ -297,7 +297,7 @@ export function updateContainer(
297
}
298
}
299
300
- const update = createUpdate(eventTime, lane, suspenseConfig);
300
+ const update = createUpdate(eventTime, lane);
301
// Caution: React DevTools currently depends on this property
302
// being called "element".
303
update.payload = {element};
packages/react-reconciler/src/ReactFiberReconciler.old.js
+1
-1
@@ -297,7 +297,7 @@ export function updateContainer(
297
}
298
}
299
300
- const update = createUpdate(eventTime, lane, suspenseConfig);
300
+ const update = createUpdate(eventTime, lane);
301
// Caution: React DevTools currently depends on this property
302
// being called "element".
303
update.payload = {element};
packages/react-reconciler/src/ReactFiberThrow.new.js
+3
-3
@@ -76,7 +76,7 @@ function createRootErrorUpdate(
76
errorInfo: CapturedValue<mixed>,
77
lane: Lane,
78
): Update<mixed> {
79
- const update = createUpdate(NoTimestamp, lane, null);
79
+ const update = createUpdate(NoTimestamp, lane);
80
// Unmount the root by rendering null.
81
update.tag = CaptureUpdate;
82
// Caution: React DevTools currently depends on this property
@@ -95,7 +95,7 @@ function createClassErrorUpdate(
95
errorInfo: CapturedValue<mixed>,
96
lane: Lane,
97
): Update<mixed> {
98
- const update = createUpdate(NoTimestamp, lane, null);
98
+ const update = createUpdate(NoTimestamp, lane);
99
update.tag = CaptureUpdate;
100
const getDerivedStateFromError = fiber.type.getDerivedStateFromError;
101
if (typeof getDerivedStateFromError === 'function') {
@@ -274,7 +274,7 @@ function throwException(
274
// When we try rendering again, we should not reuse the current fiber,
275
// since it's known to be in an inconsistent state. Use a force update to
276
// prevent a bail out.
277
- const update = createUpdate(NoTimestamp, SyncLane, null);
277
+ const update = createUpdate(NoTimestamp, SyncLane);
278
update.tag = ForceUpdate;
279
enqueueUpdate(sourceFiber, update);
280
}
packages/react-reconciler/src/ReactFiberThrow.old.js
+3
-3
@@ -76,7 +76,7 @@ function createRootErrorUpdate(
76
errorInfo: CapturedValue<mixed>,
77
lane: Lane,
78
): Update<mixed> {
79
- const update = createUpdate(NoTimestamp, lane, null);
79
+ const update = createUpdate(NoTimestamp, lane);
80
// Unmount the root by rendering null.
81
update.tag = CaptureUpdate;
82
// Caution: React DevTools currently depends on this property
@@ -95,7 +95,7 @@ function createClassErrorUpdate(
95
errorInfo: CapturedValue<mixed>,
96
lane: Lane,
97
): Update<mixed> {
98
- const update = createUpdate(NoTimestamp, lane, null);
98
+ const update = createUpdate(NoTimestamp, lane);
99
update.tag = CaptureUpdate;
100
const getDerivedStateFromError = fiber.type.getDerivedStateFromError;
101
if (typeof getDerivedStateFromError === 'function') {
@@ -276,7 +276,7 @@ function throwException(
276
// When we try rendering again, we should not reuse the current fiber,
277
// since it's known to be in an inconsistent state. Use a force update to
278
// prevent a bail out.
279
- const update = createUpdate(NoTimestamp, SyncLane, null);
279
+ const update = createUpdate(NoTimestamp, SyncLane);
280
update.tag = ForceUpdate;
281
enqueueUpdate(sourceFiber, update);
282
}
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+2
-80
@@ -171,6 +171,7 @@ import {
171
hasDiscreteLanes,
172
includesNonIdleWork,
173
includesOnlyRetries,
174
+ includesOnlyTransitions,
175
getNextLanes,
176
returnNextLanesPriority,
177
setCurrentUpdateLanePriority,
@@ -302,8 +303,6 @@ const subtreeRenderLanesCursor: StackCursor<Lanes> = createCursor(NoLanes);
303
let workInProgressRootExitStatus: RootExitStatus = RootIncomplete;
304
// A fatal error, if one is thrown
305
let workInProgressRootFatalError: mixed = null;
305
-let workInProgressRootLatestSuspenseTimeout: number = NoTimestamp;
306
-let workInProgressRootCanSuspendUsingConfig: null | SuspenseConfig = null;
306
// "Included" lanes refer to lanes that were worked on during this render. It's
307
// slightly different than `renderLanes` because `renderLanes` can change as you
308
// enter and exit an Offscreen tree. This value is the combination of all render
@@ -323,7 +322,6 @@ let mostRecentlyUpdatedRoot: FiberRoot | null = null;
322
// model where we don't commit new loading states in too quick succession.
323
let globalMostRecentFallbackTime: number = 0;
324
const FALLBACK_THROTTLE_MS: number = 500;
326
-const DEFAULT_TIMEOUT_MS: number = 5000;
325
326
// The absolute time for when we should start giving up on rendering
327
// more and prefer CPU suspense heuristics instead.
@@ -923,12 +921,10 @@ function finishConcurrentRender(root, exitStatus, lanes) {
921
case RootSuspendedWithDelay: {
922
markRootSuspended(root, lanes);
923
926
- if (workInProgressRootLatestSuspenseTimeout !== NoTimestamp) {
924
+ if (includesOnlyTransitions(lanes)) {
925
// This is a transition, so we should exit without committing a
926
// placeholder and without scheduling a timeout. Delay indefinitely
927
// until we receive more data.
930
- // TODO: Check the lanes to see if it's a transition, instead of
931
- // tracking the latest timeout.
928
break;
929
}
930
@@ -963,30 +959,6 @@ function finishConcurrentRender(root, exitStatus, lanes) {
959
}
960
case RootCompleted: {
961
// The work completed. Ready to commit.
966
- const mostRecentEventTime = getMostRecentEventTime(root, lanes);
967
- if (
968
- // do not delay if we're inside an act() scope
969
- !shouldForceFlushFallbacksInDEV() &&
970
- mostRecentEventTime !== NoTimestamp &&
971
- workInProgressRootCanSuspendUsingConfig !== null
972
- ) {
973
- // If we have exceeded the minimum loading delay, which probably
974
- // means we have shown a spinner already, we might have to suspend
975
- // a bit longer to ensure that the spinner is shown for
976
- // enough time.
977
- const msUntilTimeout = computeMsUntilSuspenseLoadingDelay(
978
- mostRecentEventTime,
979
- workInProgressRootCanSuspendUsingConfig,
980
- );
981
- if (msUntilTimeout > 10) {
982
- markRootSuspended(root, lanes);
983
- root.timeoutHandle = scheduleTimeout(
984
- commitRoot.bind(null, root),
985
- msUntilTimeout,
986
- );
987
- break;
988
- }
989
- }
962
commitRoot(root);
963
break;
964
}
@@ -1369,8 +1341,6 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes) {
1341
workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
1342
workInProgressRootExitStatus = RootIncomplete;
1343
workInProgressRootFatalError = null;
1372
- workInProgressRootLatestSuspenseTimeout = NoTimestamp;
1373
- workInProgressRootCanSuspendUsingConfig = null;
1344
workInProgressRootSkippedLanes = NoLanes;
1345
workInProgressRootUpdatedLanes = NoLanes;
1346
workInProgressRootPingedLanes = NoLanes;
@@ -1482,30 +1452,6 @@ export function markCommitTimeOfFallback() {
1452
globalMostRecentFallbackTime = now();
1453
}
1454
1485
-export function markRenderEventTimeAndConfig(
1486
- eventTime: number,
1487
- suspenseConfig: null | SuspenseConfig,
1488
-): void {
1489
- // Track the largest/latest timeout deadline in this batch.
1490
- // TODO: If there are two transitions in the same batch, shouldn't we
1491
- // choose the smaller one? Maybe this is because when an intermediate
1492
- // transition is superseded, we should ignore its suspense config, but
1493
- // we don't currently.
1494
- if (suspenseConfig !== null) {
1495
- // If `timeoutMs` is not specified, we default to 5 seconds. We have to
1496
- // resolve this default here because `suspenseConfig` is owned
1497
- // by userspace.
1498
- // TODO: Store this on the root instead (transition -> timeoutMs)
1499
- // TODO: Should this default to a JND instead?
1500
- const timeoutMs = suspenseConfig.timeoutMs | 0 || DEFAULT_TIMEOUT_MS;
1501
- const timeoutTime = eventTime + timeoutMs;
1502
- if (timeoutTime > workInProgressRootLatestSuspenseTimeout) {
1503
- workInProgressRootLatestSuspenseTimeout = timeoutTime;
1504
- workInProgressRootCanSuspendUsingConfig = suspenseConfig;
1505
- }
1506
- }
1507
-}
1508
-
1455
export function markSkippedUpdateLanes(lane: Lane | Lanes): void {
1456
workInProgressRootSkippedLanes = mergeLanes(
1457
lane,
@@ -3079,30 +3025,6 @@ function jnd(timeElapsed: number) {
3025
: ceil(timeElapsed / 1960) * 1960;
3026
}
3027
3082
-function computeMsUntilSuspenseLoadingDelay(
3083
- mostRecentEventTime: number,
3084
- suspenseConfig: SuspenseConfig,
3085
-) {
3086
- const busyMinDurationMs = (suspenseConfig.busyMinDurationMs: any) | 0;
3087
- if (busyMinDurationMs <= 0) {
3088
- return 0;
3089
- }
3090
- const busyDelayMs = (suspenseConfig.busyDelayMs: any) | 0;
3091
-
3092
- // Compute the time until this render pass would expire.
3093
- const currentTimeMs: number = now();
3094
- const eventTimeMs: number = mostRecentEventTime;
3095
- const timeElapsed = currentTimeMs - eventTimeMs;
3096
- if (timeElapsed <= busyDelayMs) {
3097
- // If we haven't yet waited longer than the initial delay, we don't
3098
- // have to wait any additional time.
3099
- return 0;
3100
- }
3101
- const msUntilTimeout = busyDelayMs + busyMinDurationMs - timeElapsed;
3102
- // This is the value that is passed to `setTimeout`.
3103
- return msUntilTimeout;
3104
-}
3105
-
3028
function checkForNestedUpdates() {
3029
if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
3030
nestedUpdateCount = 0;
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+2
-80
@@ -157,6 +157,7 @@ import {
157
hasDiscreteLanes,
158
includesNonIdleWork,
159
includesOnlyRetries,
160
+ includesOnlyTransitions,
161
getNextLanes,
162
returnNextLanesPriority,
163
setCurrentUpdateLanePriority,
@@ -287,8 +288,6 @@ const subtreeRenderLanesCursor: StackCursor<Lanes> = createCursor(NoLanes);
288
let workInProgressRootExitStatus: RootExitStatus = RootIncomplete;
289
// A fatal error, if one is thrown
290
let workInProgressRootFatalError: mixed = null;
290
-let workInProgressRootLatestSuspenseTimeout: number = NoTimestamp;
291
-let workInProgressRootCanSuspendUsingConfig: null | SuspenseConfig = null;
291
// "Included" lanes refer to lanes that were worked on during this render. It's
292
// slightly different than `renderLanes` because `renderLanes` can change as you
293
// enter and exit an Offscreen tree. This value is the combination of all render
@@ -308,7 +307,6 @@ let mostRecentlyUpdatedRoot: FiberRoot | null = null;
307
// model where we don't commit new loading states in too quick succession.
308
let globalMostRecentFallbackTime: number = 0;
309
const FALLBACK_THROTTLE_MS: number = 500;
311
-const DEFAULT_TIMEOUT_MS: number = 5000;
310
311
// The absolute time for when we should start giving up on rendering
312
// more and prefer CPU suspense heuristics instead.
@@ -911,12 +909,10 @@ function finishConcurrentRender(root, exitStatus, lanes) {
909
case RootSuspendedWithDelay: {
910
markRootSuspended(root, lanes);
911
914
- if (workInProgressRootLatestSuspenseTimeout !== NoTimestamp) {
912
+ if (includesOnlyTransitions(lanes)) {
913
// This is a transition, so we should exit without committing a
914
// placeholder and without scheduling a timeout. Delay indefinitely
915
// until we receive more data.
918
- // TODO: Check the lanes to see if it's a transition, instead of
919
- // tracking the latest timeout.
916
break;
917
}
918
@@ -951,30 +947,6 @@ function finishConcurrentRender(root, exitStatus, lanes) {
947
}
948
case RootCompleted: {
949
// The work completed. Ready to commit.
954
- const mostRecentEventTime = getMostRecentEventTime(root, lanes);
955
- if (
956
- // do not delay if we're inside an act() scope
957
- !shouldForceFlushFallbacksInDEV() &&
958
- mostRecentEventTime !== NoTimestamp &&
959
- workInProgressRootCanSuspendUsingConfig !== null
960
- ) {
961
- // If we have exceeded the minimum loading delay, which probably
962
- // means we have shown a spinner already, we might have to suspend
963
- // a bit longer to ensure that the spinner is shown for
964
- // enough time.
965
- const msUntilTimeout = computeMsUntilSuspenseLoadingDelay(
966
- mostRecentEventTime,
967
- workInProgressRootCanSuspendUsingConfig,
968
- );
969
- if (msUntilTimeout > 10) {
970
- markRootSuspended(root, lanes);
971
- root.timeoutHandle = scheduleTimeout(
972
- commitRoot.bind(null, root),
973
- msUntilTimeout,
974
- );
975
- break;
976
- }
977
- }
950
commitRoot(root);
951
break;
952
}
@@ -1357,8 +1329,6 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes) {
1329
workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
1330
workInProgressRootExitStatus = RootIncomplete;
1331
workInProgressRootFatalError = null;
1360
- workInProgressRootLatestSuspenseTimeout = NoTimestamp;
1361
- workInProgressRootCanSuspendUsingConfig = null;
1332
workInProgressRootSkippedLanes = NoLanes;
1333
workInProgressRootUpdatedLanes = NoLanes;
1334
workInProgressRootPingedLanes = NoLanes;
@@ -1470,30 +1440,6 @@ export function markCommitTimeOfFallback() {
1440
globalMostRecentFallbackTime = now();
1441
}
1442
1473
-export function markRenderEventTimeAndConfig(
1474
- eventTime: number,
1475
- suspenseConfig: null | SuspenseConfig,
1476
-): void {
1477
- // Track the largest/latest timeout deadline in this batch.
1478
- // TODO: If there are two transitions in the same batch, shouldn't we
1479
- // choose the smaller one? Maybe this is because when an intermediate
1480
- // transition is superseded, we should ignore its suspense config, but
1481
- // we don't currently.
1482
- if (suspenseConfig !== null) {
1483
- // If `timeoutMs` is not specified, we default to 5 seconds. We have to
1484
- // resolve this default here because `suspenseConfig` is owned
1485
- // by userspace.
1486
- // TODO: Store this on the root instead (transition -> timeoutMs)
1487
- // TODO: Should this default to a JND instead?
1488
- const timeoutMs = suspenseConfig.timeoutMs | 0 || DEFAULT_TIMEOUT_MS;
1489
- const timeoutTime = eventTime + timeoutMs;
1490
- if (timeoutTime > workInProgressRootLatestSuspenseTimeout) {
1491
- workInProgressRootLatestSuspenseTimeout = timeoutTime;
1492
- workInProgressRootCanSuspendUsingConfig = suspenseConfig;
1493
- }
1494
- }
1495
-}
1496
-
1443
export function markSkippedUpdateLanes(lane: Lane | Lanes): void {
1444
workInProgressRootSkippedLanes = mergeLanes(
1445
lane,
@@ -3025,30 +2971,6 @@ function jnd(timeElapsed: number) {
2971
: ceil(timeElapsed / 1960) * 1960;
2972
}
2973
3028
-function computeMsUntilSuspenseLoadingDelay(
3029
- mostRecentEventTime: number,
3030
- suspenseConfig: SuspenseConfig,
3031
-) {
3032
- const busyMinDurationMs = (suspenseConfig.busyMinDurationMs: any) | 0;
3033
- if (busyMinDurationMs <= 0) {
3034
- return 0;
3035
- }
3036
- const busyDelayMs = (suspenseConfig.busyDelayMs: any) | 0;
3037
-
3038
- // Compute the time until this render pass would expire.
3039
- const currentTimeMs: number = now();
3040
- const eventTimeMs: number = mostRecentEventTime;
3041
- const timeElapsed = currentTimeMs - eventTimeMs;
3042
- if (timeElapsed <= busyDelayMs) {
3043
- // If we haven't yet waited longer than the initial delay, we don't
3044
- // have to wait any additional time.
3045
- return 0;
3046
- }
3047
- const msUntilTimeout = busyDelayMs + busyMinDurationMs - timeElapsed;
3048
- // This is the value that is passed to `setTimeout`.
3049
- return msUntilTimeout;
3050
-}
3051
-
2974
function checkForNestedUpdates() {
2975
if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
2976
nestedUpdateCount = 0;
packages/react-reconciler/src/ReactUpdateQueue.new.js
+2
-23
@@ -86,7 +86,6 @@
86
87
import type {Fiber} from './ReactInternalTypes';
88
import type {Lanes, Lane} from './ReactFiberLane';
89
-import type {SuspenseConfig} from './ReactFiberSuspenseConfig';
89
90
import {NoLane, NoLanes, isSubsetOfLanes, mergeLanes} from './ReactFiberLane';
91
import {
@@ -98,10 +97,7 @@ import {Callback, ShouldCapture, DidCapture} from './ReactSideEffectTags';
97
import {debugRenderPhaseSideEffectsForStrictMode} from 'shared/ReactFeatureFlags';
98
99
import {StrictMode} from './ReactTypeOfMode';
101
-import {
102
- markRenderEventTimeAndConfig,
103
- markSkippedUpdateLanes,
104
-} from './ReactFiberWorkLoop.new';
100
+import {markSkippedUpdateLanes} from './ReactFiberWorkLoop.new';
101
102
import invariant from 'shared/invariant';
103
@@ -112,7 +108,6 @@ export type Update<State> = {|
108
// transition -> event time on the root.
109
eventTime: number,
110
lane: Lane,
115
- suspenseConfig: null | SuspenseConfig,
111
112
tag: 0 | 1 | 2 | 3,
113
payload: any,
@@ -186,15 +181,10 @@ export function cloneUpdateQueue<State>(
181
}
182
}
183
189
-export function createUpdate(
190
- eventTime: number,
191
- lane: Lane,
192
- suspenseConfig: null | SuspenseConfig,
193
-): Update<*> {
184
+export function createUpdate(eventTime: number, lane: Lane): Update<*> {
185
const update: Update<*> = {
186
eventTime,
187
lane,
197
- suspenseConfig,
188
189
tag: UpdateState,
190
payload: null,
@@ -269,7 +259,6 @@ export function enqueueCapturedUpdate<State>(
259
const clone: Update<State> = {
260
eventTime: update.eventTime,
261
lane: update.lane,
272
- suspenseConfig: update.suspenseConfig,
262
263
tag: update.tag,
264
payload: update.payload,
@@ -482,7 +471,6 @@ export function processUpdateQueue<State>(
471
const clone: Update<State> = {
472
eventTime: updateEventTime,
473
lane: updateLane,
485
- suspenseConfig: update.suspenseConfig,
474
475
tag: update.tag,
476
payload: update.payload,
@@ -508,7 +496,6 @@ export function processUpdateQueue<State>(
496
// it. Using NoLane works because 0 is a subset of all bitmasks, so
497
// this will never be skipped by the check above.
498
lane: NoLane,
511
- suspenseConfig: update.suspenseConfig,
499
500
tag: update.tag,
501
payload: update.payload,
@@ -519,14 +506,6 @@ export function processUpdateQueue<State>(
506
newLastBaseUpdate = newLastBaseUpdate.next = clone;
507
}
508
522
- // Mark the event time of this update as relevant to this render pass.
523
- // TODO: This should ideally use the true event time of this update rather than
524
- // its priority which is a derived and not reversible value.
525
- // TODO: We should skip this update if it was already committed but currently
526
- // we have no way of detecting the difference between a committed and suspended
527
- // update here.
528
- markRenderEventTimeAndConfig(updateEventTime, update.suspenseConfig);
529
-
509
// Process this update.
510
newState = getStateFromUpdate(
511
workInProgress,
packages/react-reconciler/src/ReactUpdateQueue.old.js
+2
-23
@@ -86,7 +86,6 @@
86
87
import type {Fiber} from './ReactInternalTypes';
88
import type {Lanes, Lane} from './ReactFiberLane';
89
-import type {SuspenseConfig} from './ReactFiberSuspenseConfig';
89
90
import {NoLane, NoLanes, isSubsetOfLanes, mergeLanes} from './ReactFiberLane';
91
import {
@@ -98,10 +97,7 @@ import {Callback, ShouldCapture, DidCapture} from './ReactSideEffectTags';
97
import {debugRenderPhaseSideEffectsForStrictMode} from 'shared/ReactFeatureFlags';
98
99
import {StrictMode} from './ReactTypeOfMode';
101
-import {
102
- markRenderEventTimeAndConfig,
103
- markSkippedUpdateLanes,
104
-} from './ReactFiberWorkLoop.old';
100
+import {markSkippedUpdateLanes} from './ReactFiberWorkLoop.old';
101
102
import invariant from 'shared/invariant';
103
@@ -112,7 +108,6 @@ export type Update<State> = {|
108
// transition -> event time on the root.
109
eventTime: number,
110
lane: Lane,
115
- suspenseConfig: null | SuspenseConfig,
111
112
tag: 0 | 1 | 2 | 3,
113
payload: any,
@@ -186,15 +181,10 @@ export function cloneUpdateQueue<State>(
181
}
182
}
183
189
-export function createUpdate(
190
- eventTime: number,
191
- lane: Lane,
192
- suspenseConfig: null | SuspenseConfig,
193
-): Update<*> {
184
+export function createUpdate(eventTime: number, lane: Lane): Update<*> {
185
const update: Update<*> = {
186
eventTime,
187
lane,
197
- suspenseConfig,
188
189
tag: UpdateState,
190
payload: null,
@@ -269,7 +259,6 @@ export function enqueueCapturedUpdate<State>(
259
const clone: Update<State> = {
260
eventTime: update.eventTime,
261
lane: update.lane,
272
- suspenseConfig: update.suspenseConfig,
262
263
tag: update.tag,
264
payload: update.payload,
@@ -482,7 +471,6 @@ export function processUpdateQueue<State>(
471
const clone: Update<State> = {
472
eventTime: updateEventTime,
473
lane: updateLane,
485
- suspenseConfig: update.suspenseConfig,
474
475
tag: update.tag,
476
payload: update.payload,
@@ -508,7 +496,6 @@ export function processUpdateQueue<State>(
496
// it. Using NoLane works because 0 is a subset of all bitmasks, so
497
// this will never be skipped by the check above.
498
lane: NoLane,
511
- suspenseConfig: update.suspenseConfig,
499
500
tag: update.tag,
501
payload: update.payload,
@@ -519,14 +506,6 @@ export function processUpdateQueue<State>(
506
newLastBaseUpdate = newLastBaseUpdate.next = clone;
507
}
508
522
- // Mark the event time of this update as relevant to this render pass.
523
- // TODO: This should ideally use the true event time of this update rather than
524
- // its priority which is a derived and not reversible value.
525
- // TODO: We should skip this update if it was already committed but currently
526
- // we have no way of detecting the difference between a committed and suspended
527
- // update here.
528
- markRenderEventTimeAndConfig(updateEventTime, update.suspenseConfig);
529
-
509
// Process this update.
510
newState = getStateFromUpdate(
511
workInProgress,
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
-79
@@ -3318,85 +3318,6 @@ describe('ReactHooksWithNoopRenderer', () => {
3318
]);
3319
});
3320
});
3321
-
3322
- // @gate experimental
3323
- it('delays showing loading state until after busyDelayMs + busyMinDurationMs', async () => {
3324
- let transition;
3325
- function App() {
3326
- const [show, setShow] = useState(false);
3327
- const [startTransition, isPending] = useTransition({
3328
- busyDelayMs: 1000,
3329
- busyMinDurationMs: 2000,
3330
- });
3331
- transition = () => {
3332
- startTransition(() => {
3333
- setShow(true);
3334
- });
3335
- };
3336
- return (
3337
- <Suspense
3338
- fallback={<Text text={`Loading... Pending: ${isPending}`} />}>
3339
- {show ? (
3340
- <AsyncText text={`After... Pending: ${isPending}`} />
3341
- ) : (
3342
- <Text text={`Before... Pending: ${isPending}`} />
3343
- )}
3344
- </Suspense>
3345
- );
3346
- }
3347
- ReactNoop.render(<App />);
3348
- expect(Scheduler).toFlushAndYield(['Before... Pending: false']);
3349
- expect(ReactNoop.getChildren()).toEqual([
3350
- span('Before... Pending: false'),
3351
- ]);
3352
-
3353
- await act(async () => {
3354
- Scheduler.unstable_runWithPriority(
3355
- Scheduler.unstable_UserBlockingPriority,
3356
- transition,
3357
- );
3358
-
3359
- expect(Scheduler).toFlushAndYield([
3360
- 'Before... Pending: true',
3361
- 'Suspend! [After... Pending: false]',
3362
- 'Loading... Pending: false',
3363
- ]);
3364
- expect(ReactNoop.getChildren()).toEqual([
3365
- span('Before... Pending: true'),
3366
- ]);
3367
-
3368
- Scheduler.unstable_advanceTime(1000);
3369
- await advanceTimers(1000);
3370
-
3371
- // Resolve the promise. The whole tree has now completed. However,
3372
- // because we exceeded the busy threshold, we won't commit the
3373
- // result yet.
3374
- Scheduler.unstable_advanceTime(1000);
3375
- await advanceTimers(1000);
3376
- await resolveText('After... Pending: false');
3377
- expect(Scheduler).toHaveYielded([
3378
- 'Promise resolved [After... Pending: false]',
3379
- ]);
3380
- expect(Scheduler).toFlushAndYield(['After... Pending: false']);
3381
- expect(ReactNoop.getChildren()).toEqual([
3382
- span('Before... Pending: true'),
3383
- ]);
3384
-
3385
- // Advance time until just before the `busyMinDuration` threshold.
3386
- Scheduler.unstable_advanceTime(999);
3387
- await advanceTimers(999);
3388
- expect(ReactNoop.getChildren()).toEqual([
3389
- span('Before... Pending: true'),
3390
- ]);
3391
-
3392
- // Advance time just a bit more. Now we complete the transition.
3393
- Scheduler.unstable_advanceTime(300);
3394
- await advanceTimers(300);
3395
- expect(ReactNoop.getChildren()).toEqual([
3396
- span('After... Pending: false'),
3397
- ]);
3398
- });
3399
- });
3321
});
3322
3323
describe('useDeferredValue', () => {
packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js
-68
@@ -2860,74 +2860,6 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2860
);
2861
});
2862
2863
- // @gate experimental
2864
- it('supports delaying a busy spinner from disappearing', async () => {
2865
- const SUSPENSE_CONFIG = {
2866
- timeoutMs: 10000,
2867
- busyDelayMs: 500,
2868
- busyMinDurationMs: 400,
2869
- };
2870
-
2871
- let transitionToPage;
2872
-
2873
- function App() {
2874
- const [page, setPage] = React.useState('A');
2875
- const [startLoading, isLoading] = React.unstable_useTransition(
2876
- SUSPENSE_CONFIG,
2877
- );
2878
- transitionToPage = nextPage => startLoading(() => setPage(nextPage));
2879
- return (
2880
- <Fragment>
2881
- <Text text={page} />
2882
- {isLoading ? <Text text="L" /> : null}
2883
- </Fragment>
2884
- );
2885
- }
2886
-
2887
- // Initial render.
2888
- ReactNoop.render(<App />);
2889
- expect(Scheduler).toFlushAndYield(['A']);
2890
- expect(ReactNoop.getChildren()).toEqual([span('A')]);
2891
-
2892
- await ReactNoop.act(async () => {
2893
- transitionToPage('B');
2894
- // Rendering B is quick and we didn't have enough
2895
- // time to show the loading indicator.
2896
- Scheduler.unstable_advanceTime(200);
2897
- await advanceTimers(200);
2898
- expect(Scheduler).toFlushAndYield(['A', 'L', 'B']);
2899
- expect(ReactNoop.getChildren()).toEqual([span('B')]);
2900
- });
2901
-
2902
- await ReactNoop.act(async () => {
2903
- transitionToPage('C');
2904
- // Rendering C is a bit slower so we've already showed
2905
- // the loading indicator.
2906
- Scheduler.unstable_advanceTime(600);
2907
- await advanceTimers(600);
2908
- expect(Scheduler).toFlushAndYield(['B', 'L', 'C']);
2909
- // We're technically done now but we haven't shown the
2910
- // loading indicator for long enough yet so we'll suspend
2911
- // while we keep it on the screen a bit longer.
2912
- expect(ReactNoop.getChildren()).toEqual([span('B'), span('L')]);
2913
- Scheduler.unstable_advanceTime(400);
2914
- await advanceTimers(400);
2915
- expect(ReactNoop.getChildren()).toEqual([span('C')]);
2916
- });
2917
-
2918
- await ReactNoop.act(async () => {
2919
- transitionToPage('D');
2920
- // Rendering D is very slow so we've already showed
2921
- // the loading indicator.
2922
- Scheduler.unstable_advanceTime(1000);
2923
- await advanceTimers(1000);
2924
- expect(Scheduler).toFlushAndYield(['C', 'L', 'D']);
2925
- // However, since we exceeded the minimum time to show
2926
- // the loading indicator, we commit immediately.
2927
- expect(ReactNoop.getChildren()).toEqual([span('D')]);
2928
- });
2929
- });
2930
-
2863
it("suspended commit remains suspended even if there's another update at same expiration", async () => {
2864
// Regression test
2865
function App({text}) {