86
resetRendererAfterRender,
87
startSuspendingCommit,
88
waitForCommitToBeReady,
89
+ preloadInstance,
90
} from './ReactFiberHostConfig';
91
92
import {
115
MemoComponent,
116
SimpleMemoComponent,
117
Profiler,
118
+ HostComponent,
119
+ HostHoistable,
120
+ HostSingleton,
121
} from './ReactWorkTags';
122
import {ConcurrentRoot, LegacyRoot} from './ReactRootTags';
123
import type {Flags} from './ReactFiberFlags';
277
import {processTransitionCallbacks} from './ReactFiberTracingMarkerComponent';
278
import {
279
SuspenseException,
280
+ SuspenseyCommitException,
281
getSuspendedThenable,
282
isThenableResolved,
283
} from './ReactFiberThenable';
326
// The lanes we're rendering
327
let workInProgressRootRenderLanes: Lanes = NoLanes;
328
324
-opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4 | 5 | 6;
329
+opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
330
const NotSuspended: SuspendedReason = 0;
331
const SuspendedOnError: SuspendedReason = 1;
332
const SuspendedOnData: SuspendedReason = 2;
333
const SuspendedOnImmediate: SuspendedReason = 3;
329
-const SuspendedOnDeprecatedThrowPromise: SuspendedReason = 4;
330
-const SuspendedAndReadyToContinue: SuspendedReason = 5;
331
-const SuspendedOnHydration: SuspendedReason = 6;
334
+const SuspendedOnInstance: SuspendedReason = 4;
335
+const SuspendedOnInstanceAndReadyToContinue: SuspendedReason = 5;
336
+const SuspendedOnDeprecatedThrowPromise: SuspendedReason = 6;
337
+const SuspendedAndReadyToContinue: SuspendedReason = 7;
338
+const SuspendedOnHydration: SuspendedReason = 8;
339
340
// When this is true, the work-in-progress fiber just suspended (or errored) and
341
// we've yet to unwind the stack. In some cases, we may yield to the main thread
1878
// immediately resolved (i.e. in a microtask). Otherwise, trigger the
1879
// nearest Suspense fallback.
1880
SuspendedOnImmediate;
1881
+ } else if (thrownValue === SuspenseyCommitException) {
1882
+ thrownValue = getSuspendedThenable();
1883
+ workInProgressSuspendedReason = SuspendedOnInstance;
1884
} else if (thrownValue === SelectiveHydrationException) {
1885
// An update flowed into a dehydrated boundary. Before we can apply the
1886
// update, we need to finish hydrating. Interrupt the work-in-progress
1948
);
1949
break;
1950
}
1951
+ case SuspendedOnInstance: {
1952
+ // This is conceptually like a suspend, but it's not associated with
1953
+ // a particular wakeable. It's associated with a host resource (e.g.
1954
+ // a CSS file or an image) that hasn't loaded yet. DevTools doesn't
1955
+ // handle this currently.
1956
+ break;
1957
+ }
1958
case SuspendedOnHydration: {
1959
// This is conceptually like a suspend, but it's not associated with
1960
// a particular wakeable. DevTools doesn't seem to care about this case,
2280
// replay the suspended component.
2281
const unitOfWork = workInProgress;
2282
const thrownValue = workInProgressThrownValue;
2266
- switch (workInProgressSuspendedReason) {
2283
+ resumeOrUnwind: switch (workInProgressSuspendedReason) {
2284
case SuspendedOnError: {
2285
// Unwind then continue with the normal work loop.
2286
workInProgressSuspendedReason = NotSuspended;
2327
workInProgressSuspendedReason = SuspendedAndReadyToContinue;
2328
break outer;
2329
}
2330
+ case SuspendedOnInstance: {
2331
+ workInProgressSuspendedReason =
2332
+ SuspendedOnInstanceAndReadyToContinue;
2333
+ break outer;
2334
+ }
2335
case SuspendedAndReadyToContinue: {
2336
const thenable: Thenable<mixed> = (thrownValue: any);
2337
if (isThenableResolved(thenable)) {
2347
}
2348
break;
2349
}
2350
+ case SuspendedOnInstanceAndReadyToContinue: {
2351
+ switch (workInProgress.tag) {
2352
+ case HostComponent:
2353
+ case HostHoistable:
2354
+ case HostSingleton: {
2355
+ // Before unwinding the stack, check one more time if the
2356
+ // instance is ready. It may have loaded when React yielded to
2357
+ // the main thread.
2358
+
2359
+ // Assigning this to a constant so Flow knows the binding won't
2360
+ // be mutated by `preloadInstance`.
2361
+ const hostFiber = workInProgress;
2362
+ const type = hostFiber.type;
2363
+ const props = hostFiber.pendingProps;
2364
+ const isReady = preloadInstance(type, props);
2365
+ if (isReady) {
2366
+ // The data resolved. Resume the work loop as if nothing
2367
+ // suspended. Unlike when a user component suspends, we don't
2368
+ // have to replay anything because the host fiber
2369
+ // already completed.
2370
+ workInProgressSuspendedReason = NotSuspended;
2371
+ workInProgressThrownValue = null;
2372
+ const sibling = hostFiber.sibling;
2373
+ if (sibling !== null) {
2374
+ workInProgress = sibling;
2375
+ } else {
2376
+ const returnFiber = hostFiber.return;
2377
+ if (returnFiber !== null) {
2378
+ workInProgress = returnFiber;
2379
+ completeUnitOfWork(returnFiber);
2380
+ } else {
2381
+ workInProgress = null;
2382
+ }
2383
+ }
2384
+ break resumeOrUnwind;
2385
+ }
2386
+ break;
2387
+ }
2388
+ default: {
2389
+ // This will fail gracefully but it's not correct, so log a
2390
+ // warning in dev.
2391
+ if (__DEV__) {
2392
+ console.error(
2393
+ 'Unexpected type of fiber triggered a suspensey commit. ' +
2394
+ 'This is a bug in React.',
2395
+ );
2396
+ }
2397
+ break;
2398
+ }
2399
+ }
2400
+ // Otherwise, unwind then continue with the normal work loop.
2401
+ workInProgressSuspendedReason = NotSuspended;
2402
+ workInProgressThrownValue = null;
2403
+ unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
2404
+ break;
2405
+ }
2406
case SuspendedOnDeprecatedThrowPromise: {
2407
// Suspended by an old implementation that uses the `throw promise`
2408
// pattern. The newer replaying behavior can cause subtle issues