mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Restart from root if promise pings before end of render phase (#13774)
* Restart from root if promise pings before end of render phase * Test that placeholder resolves successfully even if fallback render is pending
This commit is contained in:
+9
-1
@@ -111,7 +111,7 @@ import {
|
||||
computeAsyncExpiration,
|
||||
computeInteractiveExpiration,
|
||||
} from './ReactFiberExpirationTime';
|
||||
import {ConcurrentMode, ProfileMode} from './ReactTypeOfMode';
|
||||
import {ConcurrentMode, ProfileMode, NoContext} from './ReactTypeOfMode';
|
||||
import {enqueueUpdate, resetCurrentlyProcessingQueue} from './ReactUpdateQueue';
|
||||
import {createCapturedValue} from './ReactCapturedValue';
|
||||
import {
|
||||
@@ -1597,6 +1597,14 @@ function retrySuspendedRoot(
|
||||
markPendingPriorityLevel(root, retryTime);
|
||||
}
|
||||
|
||||
if ((fiber.mode & ConcurrentMode) !== NoContext) {
|
||||
if (root === nextRoot && nextRenderExpirationTime === suspendedTime) {
|
||||
// Received a ping at the same priority level at which we're currently
|
||||
// rendering. Restart from the root.
|
||||
nextRoot = null;
|
||||
}
|
||||
}
|
||||
|
||||
scheduleWorkToRoot(fiber, retryTime);
|
||||
const rootExpirationTime = root.expirationTime;
|
||||
if (rootExpirationTime !== NoWork) {
|
||||
|
||||
@@ -189,4 +189,57 @@ describe('ReactSuspense', () => {
|
||||
expect(root).toFlushAndYield(['B']);
|
||||
expect(root).toMatchRenderedOutput('AB');
|
||||
});
|
||||
|
||||
it('interrupts current render if promise resolves before current render phase', () => {
|
||||
let didResolve = false;
|
||||
let listeners = [];
|
||||
|
||||
const thenable = {
|
||||
then(resolve) {
|
||||
if (!didResolve) {
|
||||
listeners.push(resolve);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function resolveThenable() {
|
||||
didResolve = true;
|
||||
listeners.forEach(l => l());
|
||||
}
|
||||
|
||||
function Async() {
|
||||
if (!didResolve) {
|
||||
ReactTestRenderer.unstable_yield('Suspend!');
|
||||
throw thenable;
|
||||
}
|
||||
ReactTestRenderer.unstable_yield('Async');
|
||||
return 'Async';
|
||||
}
|
||||
|
||||
const root = ReactTestRenderer.create(
|
||||
<Placeholder delayMs={1000} fallback={<Text text="Loading..." />}>
|
||||
<Async />
|
||||
<Text text="Sibling" />
|
||||
</Placeholder>,
|
||||
{
|
||||
unstable_isConcurrent: true,
|
||||
},
|
||||
);
|
||||
|
||||
expect(root).toFlushAndYieldThrough(['Suspend!']);
|
||||
|
||||
// The promise resolves before the current render phase has completed
|
||||
resolveThenable();
|
||||
expect(ReactTestRenderer).toHaveYielded([]);
|
||||
|
||||
// Start over from the root, instead of continuing.
|
||||
expect(root).toFlushAndYield([
|
||||
// Async renders again *before* Sibling
|
||||
'Async',
|
||||
'Sibling',
|
||||
]);
|
||||
expect(root).toMatchRenderedOutput('AsyncSibling');
|
||||
});
|
||||
});
|
||||
|
||||
+16
@@ -649,6 +649,22 @@ describe('ReactSuspenseWithNoopRenderer', () => {
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Async'), span('Sync')]);
|
||||
});
|
||||
|
||||
it('resolves successfully even if fallback render is pending', async () => {
|
||||
ReactNoop.render(
|
||||
<Placeholder delayMs={1000} fallback={<Text text="Loading..." />}>
|
||||
<AsyncText text="Async" ms={3000} />
|
||||
</Placeholder>,
|
||||
);
|
||||
expect(ReactNoop.flushNextYield()).toEqual(['Suspend! [Async]']);
|
||||
await advanceTimers(1500);
|
||||
expect(ReactNoop.expire(1500)).toEqual([]);
|
||||
// Before we have a chance to flush, the promise resolves.
|
||||
await advanceTimers(2000);
|
||||
expect(ReactNoop.clearYields()).toEqual(['Promise resolved [Async]']);
|
||||
expect(ReactNoop.flush()).toEqual(['Async']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Async')]);
|
||||
});
|
||||
|
||||
it('throws a helpful error when an update is suspends without a placeholder', () => {
|
||||
expect(() => {
|
||||
ReactNoop.flushSync(() =>
|
||||
|
||||
Reference in New Issue
Block a user