25
TransitionAbort,
26
} from './ReactFiberTracingMarkerComponent.new';
27
import type {OffscreenInstance} from './ReactFiberOffscreenComponent';
28
-import type {ThenableState} from './ReactFiberThenable.new';
28
29
import {
30
warnAboutDeprecatedLifecycles,
274
import {
275
SuspenseException,
276
getSuspendedThenable,
278
- getThenableStateAfterSuspending,
277
isThenableResolved,
278
} from './ReactFiberThenable.new';
279
import {schedulePostPaintCallback} from './ReactPostPaintCallback';
320
// The lanes we're rendering
321
let workInProgressRootRenderLanes: Lanes = NoLanes;
322
325
-opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4 | 5;
323
+opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4 | 5 | 6;
324
const NotSuspended: SuspendedReason = 0;
325
const SuspendedOnError: SuspendedReason = 1;
326
const SuspendedOnData: SuspendedReason = 2;
327
const SuspendedOnImmediate: SuspendedReason = 3;
330
-const SuspendedAndReadyToUnwind: SuspendedReason = 4;
331
-const SuspendedOnHydration: SuspendedReason = 5;
328
+const SuspendedOnDeprecatedThrowPromise: SuspendedReason = 4;
329
+const SuspendedAndReadyToUnwind: SuspendedReason = 5;
330
+const SuspendedOnHydration: SuspendedReason = 6;
331
332
// When this is true, the work-in-progress fiber just suspended (or errored) and
333
// we've yet to unwind the stack. In some cases, we may yield to the main thread
335
// immediately instead of unwinding the stack.
336
let workInProgressSuspendedReason: SuspendedReason = NotSuspended;
337
let workInProgressThrownValue: mixed = null;
339
-let workInProgressSuspendedThenableState: ThenableState | null = null;
338
339
// Whether a ping listener was attached during this render. This is slightly
340
// different that whether something suspended, because we don't add multiple
1747
workInProgressRootRenderLanes = renderLanes = lanes;
1748
workInProgressSuspendedReason = NotSuspended;
1749
workInProgressThrownValue = null;
1752
- workInProgressSuspendedThenableState = null;
1750
workInProgressRootDidAttachPingListener = false;
1751
workInProgressRootExitStatus = RootInProgress;
1752
workInProgressRootFatalError = null;
1799
// API for suspending. This implementation detail can change later, once we
1800
// deprecate the old API in favor of `use`.
1801
thrownValue = getSuspendedThenable();
1805
- workInProgressSuspendedThenableState = getThenableStateAfterSuspending();
1802
workInProgressSuspendedReason = shouldAttemptToSuspendUntilDataResolves()
1803
? SuspendedOnData
1804
: SuspendedOnImmediate;
1812
//
1813
// We could name this something more general but as of now it's the only
1814
// case where we think this should happen.
1819
- workInProgressSuspendedThenableState = null;
1815
workInProgressSuspendedReason = SuspendedOnHydration;
1816
} else {
1822
- // This is a regular error. If something earlier in the component already
1823
- // suspended, we must clear the thenable state to unblock the work loop.
1824
- workInProgressSuspendedThenableState = null;
1825
-
1817
+ // This is a regular error.
1818
const isWakeable =
1819
thrownValue !== null &&
1820
typeof thrownValue === 'object' &&
1824
workInProgressSuspendedReason = isWakeable
1825
? // A wakeable object was thrown by a legacy Suspense implementation.
1826
// This has slightly different behavior than suspending with `use`.
1835
- SuspendedAndReadyToUnwind
1827
+ SuspendedOnDeprecatedThrowPromise
1828
: // This is a regular error. If something earlier in the component already
1829
// suspended, we must clear the thenable state to unblock the work loop.
1830
SuspendedOnError;
2197
}
2198
case SuspendedOnData: {
2199
const thenable: Thenable<mixed> = (thrownValue: any);
2208
- if (workInProgressSuspendedThenableState !== null) {
2209
- const thenableState = workInProgressSuspendedThenableState;
2210
- if (isThenableResolved(thenable)) {
2211
- // The data resolved. Try rendering the component again.
2212
- workInProgressSuspendedReason = NotSuspended;
2213
- workInProgressThrownValue = null;
2214
- replaySuspendedUnitOfWork(unitOfWork, thenable, thenableState);
2215
- break;
2216
- }
2200
+ if (isThenableResolved(thenable)) {
2201
+ // The data resolved. Try rendering the component again.
2202
+ workInProgressSuspendedReason = NotSuspended;
2203
+ workInProgressThrownValue = null;
2204
+ replaySuspendedUnitOfWork(unitOfWork);
2205
+ break;
2206
}
2218
-
2207
// The work loop is suspended on data. We should wait for it to
2208
// resolve before continuing to render.
2209
const onResolution = () => {
2219
workInProgressSuspendedReason = SuspendedAndReadyToUnwind;
2220
break outer;
2221
}
2222
+ case SuspendedAndReadyToUnwind: {
2223
+ const thenable: Thenable<mixed> = (thrownValue: any);
2224
+ if (isThenableResolved(thenable)) {
2225
+ // The data resolved. Try rendering the component again.
2226
+ workInProgressSuspendedReason = NotSuspended;
2227
+ workInProgressThrownValue = null;
2228
+ replaySuspendedUnitOfWork(unitOfWork);
2229
+ } else {
2230
+ // Otherwise, unwind then continue with the normal work loop.
2231
+ workInProgressSuspendedReason = NotSuspended;
2232
+ workInProgressThrownValue = null;
2233
+ unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
2234
+ }
2235
+ break;
2236
+ }
2237
+ case SuspendedOnDeprecatedThrowPromise: {
2238
+ // Suspended by an old implementation that uses the `throw promise`
2239
+ // pattern. The newer replaying behavior can cause subtle issues
2240
+ // like infinite ping loops. So we maintain the old behavior and
2241
+ // always unwind.
2242
+ workInProgressSuspendedReason = NotSuspended;
2243
+ workInProgressThrownValue = null;
2244
+ unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
2245
+ break;
2246
+ }
2247
case SuspendedOnHydration: {
2248
// Selective hydration. An update flowed into a dehydrated tree.
2249
// Interrupt the current render so the work loop can switch to the
2253
break outer;
2254
}
2255
default: {
2243
- if (workInProgressSuspendedThenableState !== null) {
2244
- const thenableState = workInProgressSuspendedThenableState;
2245
- const thenable: Thenable<mixed> = (thrownValue: any);
2246
- if (isThenableResolved(thenable)) {
2247
- // The data resolved. Try rendering the component again.
2248
- workInProgressSuspendedReason = NotSuspended;
2249
- workInProgressThrownValue = null;
2250
- replaySuspendedUnitOfWork(
2251
- unitOfWork,
2252
- thrownValue,
2253
- thenableState,
2254
- );
2255
- break;
2256
- }
2257
- }
2258
-
2259
- // Otherwise, unwind then continue with the normal work loop.
2260
- workInProgressSuspendedReason = NotSuspended;
2261
- workInProgressThrownValue = null;
2262
- unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
2263
- break;
2256
+ throw new Error(
2257
+ 'Unexpected SuspendedReason. This is a bug in React.',
2258
+ );
2259
}
2260
}
2261
}
2339
ReactCurrentOwner.current = null;
2340
}
2341
2347
-function replaySuspendedUnitOfWork(
2348
- unitOfWork: Fiber,
2349
- thrownValue: mixed,
2350
- thenableState: ThenableState,
2351
-): void {
2342
+function replaySuspendedUnitOfWork(unitOfWork: Fiber): void {
2343
// This is a fork of performUnitOfWork specifcally for replaying a fiber that
2344
// just suspended.
2345
//
2378
unitOfWork,
2379
resolvedProps,
2380
Component,
2390
- thenableState,
2381
workInProgressRootRenderLanes,
2382
);
2383
break;
2390
unitOfWork,
2391
nextProps,
2392
Component,
2403
- thenableState,
2393
workInProgressRootRenderLanes,
2394
);
2395
break;
2416
stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
2417
}
2418
2430
- // The begin phase finished successfully without suspending. Reset the state
2431
- // used to track the fiber while it was suspended. Then return to the normal
2432
- // work loop.
2433
- workInProgressSuspendedThenableState = null;
2419
+ // The begin phase finished successfully without suspending. Return to the
2420
+ // normal work loop.
2421
2422
resetCurrentDebugFiberInDEV();
2423
unitOfWork.memoizedProps = unitOfWork.pendingProps;
2437
//
2438
// Return to the normal work loop. This will unwind the stack, and potentially
2439
// result in showing a fallback.
2453
- workInProgressSuspendedThenableState = null;
2440
resetSuspendedWorkLoopOnUnwind();
2441
2442
const returnFiber = unitOfWork.return;
2480
completeUnitOfWork(unitOfWork);
2481
}
2482
2497
-export function getSuspendedThenableState(): ThenableState | null {
2498
- return workInProgressSuspendedThenableState;
2499
-}
2500
-
2483
function completeUnitOfWork(unitOfWork: Fiber): void {
2484
// Attempt to complete the current unit of work, then move to the next
2485
// sibling. If there are no more siblings, return to the parent fiber.