diff --git a/packages/react-reconciler/src/ReactFiberScheduler.js b/packages/react-reconciler/src/ReactFiberScheduler.js
index f748228f8e..7864aa0f9a 100644
--- a/packages/react-reconciler/src/ReactFiberScheduler.js
+++ b/packages/react-reconciler/src/ReactFiberScheduler.js
@@ -936,7 +936,10 @@ function renderRoot(
}
export function markRenderEventTime(expirationTime: ExpirationTime): void {
- if (expirationTime < workInProgressRootMostRecentEventTime) {
+ if (
+ expirationTime < workInProgressRootMostRecentEventTime &&
+ expirationTime > Never
+ ) {
workInProgressRootMostRecentEventTime = expirationTime;
}
}
@@ -1866,7 +1869,11 @@ function computeMsUntilTimeout(
const eventTimeMs: number = inferTimeFromExpirationTime(mostRecentEventTime);
const currentTimeMs: number = now();
- const timeElapsed = currentTimeMs - eventTimeMs;
+ let timeElapsed = currentTimeMs - eventTimeMs;
+ if (timeElapsed < 0) {
+ // We get this wrong some time since we estimate the time.
+ timeElapsed = 0;
+ }
let msUntilTimeout = jnd(timeElapsed) - timeElapsed;
diff --git a/packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js
index 7060f42730..40af1ba4c2 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js
@@ -459,7 +459,7 @@ describe('ReactSuspensePlaceholder', () => {
expect(ReactNoop).toMatchRenderedOutput('Text');
// Show the fallback UI.
- jest.advanceTimersByTime(750);
+ jest.advanceTimersByTime(900);
expect(ReactNoop).toMatchRenderedOutput('Loading...');
expect(onRender).toHaveBeenCalledTimes(2);
@@ -479,7 +479,7 @@ describe('ReactSuspensePlaceholder', () => {
-
+
,
);
@@ -495,7 +495,7 @@ describe('ReactSuspensePlaceholder', () => {
expect(onRender).toHaveBeenCalledTimes(2);
// Resolve the pending promise.
- jest.advanceTimersByTime(250);
+ jest.advanceTimersByTime(100);
expect(Scheduler).toHaveYielded([
'Promise resolved [Loaded]',
'Promise resolved [Sibling]',
diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js
index 5e0e9cac07..87e86b9495 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js
@@ -1841,4 +1841,49 @@ describe('ReactSuspenseWithNoopRenderer', () => {
expect(Scheduler).toFlushAndYield(['A', 'C']);
expect(ReactNoop.getChildren()).toEqual([span('A'), span('C'), span('B')]);
});
+
+ it('commits a suspended idle pri render within a reasonable time', async () => {
+ function Foo({something}) {
+ return (
+
+ }>
+
+
+
+ );
+ }
+
+ ReactNoop.render();
+
+ // Took a long time to render. This is to ensure we get a long suspense time.
+ // Could also use something like suspendIfNeeded to simulate this.
+ Scheduler.advanceTime(1500);
+ await advanceTimers(1500);
+
+ expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading A...']);
+ // We're still suspended.
+ expect(ReactNoop.getChildren()).toEqual([]);
+
+ // Schedule an update at idle pri.
+ Scheduler.unstable_runWithPriority(Scheduler.unstable_IdlePriority, () =>
+ ReactNoop.render(),
+ );
+ expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading A...']);
+
+ // We're still suspended.
+ expect(ReactNoop.getChildren()).toEqual([]);
+
+ // Advance time a little bit.
+ Scheduler.advanceTime(150);
+ await advanceTimers(150);
+
+ // We should not have committed yet because we had a long suspense time.
+ expect(ReactNoop.getChildren()).toEqual([]);
+
+ // Flush to skip suspended time.
+ Scheduler.advanceTime(600);
+ await advanceTimers(600);
+
+ expect(ReactNoop.getChildren()).toEqual([span('Loading A...')]);
+ });
});