@samitouri / QOS-React / commits / 1eaafc9ade

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}) {