From dd5c2082572a6bf21530b5eecd138b9a455558fe Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Sat, 22 Oct 2022 17:52:20 -0400 Subject: [PATCH] Revert yieldy behavior for non-use Suspense (#25537) To derisk the rollout of `use`, and simplify the implementation, this reverts the yield-to-microtasks behavior for promises that are thrown directly (as opposed to being unwrapped by `use`). We may add this back later. However, the plan is to deprecate throwing a promise directly and migrate all existing Suspense code to `use`, so the extra code probably isn't worth it. --- .../src/ReactFiberHooks.new.js | 3 +- .../src/ReactFiberHooks.old.js | 3 +- ...eable.new.js => ReactFiberThenable.new.js} | 54 ++++--------------- ...eable.old.js => ReactFiberThenable.old.js} | 54 ++++--------------- .../src/ReactFiberWorkLoop.new.js | 20 +++---- .../src/ReactFiberWorkLoop.old.js | 20 +++---- .../__tests__/ReactOffscreenSuspense-test.js | 42 ++++++--------- .../ReactSuspenseWithNoopRenderer-test.js | 1 + ...Wakeable-test.js => ReactThenable-test.js} | 5 ++ packages/react-server/src/ReactFizzHooks.js | 5 +- packages/react-server/src/ReactFizzServer.js | 9 +--- ...ctFizzWakeable.js => ReactFizzThenable.js} | 23 +++----- packages/react-server/src/ReactFlightHooks.js | 4 +- .../react-server/src/ReactFlightServer.js | 4 +- ...ightWakeable.js => ReactFlightThenable.js} | 2 + scripts/jest/TestFlags.js | 1 + 16 files changed, 75 insertions(+), 175 deletions(-) rename packages/react-reconciler/src/{ReactFiberWakeable.new.js => ReactFiberThenable.new.js} (68%) rename packages/react-reconciler/src/{ReactFiberWakeable.old.js => ReactFiberThenable.old.js} (68%) rename packages/react-reconciler/src/__tests__/{ReactWakeable-test.js => ReactThenable-test.js} (97%) rename packages/react-server/src/{ReactFizzWakeable.js => ReactFizzThenable.js} (87%) rename packages/react-server/src/{ReactFlightWakeable.js => ReactFlightThenable.js} (97%) diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 6b67e7497b181..2a358efc0a5a8 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -136,7 +136,7 @@ import {now} from './Scheduler'; import { trackUsedThenable, getPreviouslyUsedThenableAtIndex, -} from './ReactFiberWakeable.new'; +} from './ReactFiberThenable.new'; const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals; @@ -783,6 +783,7 @@ function use(usable: Usable): T { const index = thenableIndexCounter; thenableIndexCounter += 1; + // TODO: Unify this switch statement with the one in trackUsedThenable. switch (thenable.status) { case 'fulfilled': { const fulfilledValue: T = thenable.value; diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 5a4c86f0c0c1e..3370f51cbd412 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -136,7 +136,7 @@ import {now} from './Scheduler'; import { trackUsedThenable, getPreviouslyUsedThenableAtIndex, -} from './ReactFiberWakeable.old'; +} from './ReactFiberThenable.old'; const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals; @@ -783,6 +783,7 @@ function use(usable: Usable): T { const index = thenableIndexCounter; thenableIndexCounter += 1; + // TODO: Unify this switch statement with the one in trackUsedThenable. switch (thenable.status) { case 'fulfilled': { const fulfilledValue: T = thenable.value; diff --git a/packages/react-reconciler/src/ReactFiberWakeable.new.js b/packages/react-reconciler/src/ReactFiberThenable.new.js similarity index 68% rename from packages/react-reconciler/src/ReactFiberWakeable.new.js rename to packages/react-reconciler/src/ReactFiberThenable.new.js index 32c63740cca28..0a8f9d8deaad6 100644 --- a/packages/react-reconciler/src/ReactFiberWakeable.new.js +++ b/packages/react-reconciler/src/ReactFiberThenable.new.js @@ -8,7 +8,6 @@ */ import type { - Wakeable, Thenable, PendingThenable, FulfilledThenable, @@ -18,14 +17,8 @@ import type { import ReactSharedInternals from 'shared/ReactSharedInternals'; const {ReactCurrentActQueue} = ReactSharedInternals; -let suspendedThenable: Thenable | null = null; -let adHocSuspendCount: number = 0; - -// TODO: Sparse arrays are bad for performance. +let suspendedThenable: Thenable | null = null; let usedThenables: Array | void> | null = null; -let lastUsedThenable: Thenable | null = null; - -const MAX_AD_HOC_SUSPEND_COUNT = 50; export function isTrackingSuspendedThenable(): boolean { return suspendedThenable !== null; @@ -39,22 +32,17 @@ export function suspendedThenableDidResolve(): boolean { return false; } -export function trackSuspendedWakeable(wakeable: Wakeable) { - // If this wakeable isn't already a thenable, turn it into one now. Then, - // when we resume the work loop, we can check if its status is - // still pending. - // TODO: Get rid of the Wakeable type? It's superseded by UntrackedThenable. - const thenable: Thenable = (wakeable: any); +export function trackUsedThenable(thenable: Thenable, index: number) { + if (__DEV__ && ReactCurrentActQueue.current !== null) { + ReactCurrentActQueue.didUsePromise = true; + } - if (thenable !== lastUsedThenable) { - // If this wakeable was not just `use`-d, it must be an ad hoc wakeable - // that was thrown by an older Suspense implementation. Keep a count of - // these so that we can detect an infinite ping loop. - // TODO: Once `use` throws an opaque signal instead of the actual thenable, - // a better way to count ad hoc suspends is whether an actual thenable - // is caught by the work loop. - adHocSuspendCount++; + if (usedThenables === null) { + usedThenables = [thenable]; + } else { + usedThenables[index] = thenable; } + suspendedThenable = thenable; // We use an expando to track the status and result of a thenable so that we @@ -105,34 +93,12 @@ export function trackSuspendedWakeable(wakeable: Wakeable) { export function resetWakeableStateAfterEachAttempt() { suspendedThenable = null; - adHocSuspendCount = 0; - lastUsedThenable = null; } export function resetThenableStateOnCompletion() { usedThenables = null; } -export function throwIfInfinitePingLoopDetected() { - if (adHocSuspendCount > MAX_AD_HOC_SUSPEND_COUNT) { - // TODO: Guard against an infinite loop by throwing an error if the same - // component suspends too many times in a row. This should be thrown from - // the render phase so that it gets the component stack. - } -} - -export function trackUsedThenable(thenable: Thenable, index: number) { - if (usedThenables === null) { - usedThenables = []; - } - usedThenables[index] = thenable; - lastUsedThenable = thenable; - - if (__DEV__ && ReactCurrentActQueue.current !== null) { - ReactCurrentActQueue.didUsePromise = true; - } -} - export function getPreviouslyUsedThenableAtIndex( index: number, ): Thenable | null { diff --git a/packages/react-reconciler/src/ReactFiberWakeable.old.js b/packages/react-reconciler/src/ReactFiberThenable.old.js similarity index 68% rename from packages/react-reconciler/src/ReactFiberWakeable.old.js rename to packages/react-reconciler/src/ReactFiberThenable.old.js index 32c63740cca28..0a8f9d8deaad6 100644 --- a/packages/react-reconciler/src/ReactFiberWakeable.old.js +++ b/packages/react-reconciler/src/ReactFiberThenable.old.js @@ -8,7 +8,6 @@ */ import type { - Wakeable, Thenable, PendingThenable, FulfilledThenable, @@ -18,14 +17,8 @@ import type { import ReactSharedInternals from 'shared/ReactSharedInternals'; const {ReactCurrentActQueue} = ReactSharedInternals; -let suspendedThenable: Thenable | null = null; -let adHocSuspendCount: number = 0; - -// TODO: Sparse arrays are bad for performance. +let suspendedThenable: Thenable | null = null; let usedThenables: Array | void> | null = null; -let lastUsedThenable: Thenable | null = null; - -const MAX_AD_HOC_SUSPEND_COUNT = 50; export function isTrackingSuspendedThenable(): boolean { return suspendedThenable !== null; @@ -39,22 +32,17 @@ export function suspendedThenableDidResolve(): boolean { return false; } -export function trackSuspendedWakeable(wakeable: Wakeable) { - // If this wakeable isn't already a thenable, turn it into one now. Then, - // when we resume the work loop, we can check if its status is - // still pending. - // TODO: Get rid of the Wakeable type? It's superseded by UntrackedThenable. - const thenable: Thenable = (wakeable: any); +export function trackUsedThenable(thenable: Thenable, index: number) { + if (__DEV__ && ReactCurrentActQueue.current !== null) { + ReactCurrentActQueue.didUsePromise = true; + } - if (thenable !== lastUsedThenable) { - // If this wakeable was not just `use`-d, it must be an ad hoc wakeable - // that was thrown by an older Suspense implementation. Keep a count of - // these so that we can detect an infinite ping loop. - // TODO: Once `use` throws an opaque signal instead of the actual thenable, - // a better way to count ad hoc suspends is whether an actual thenable - // is caught by the work loop. - adHocSuspendCount++; + if (usedThenables === null) { + usedThenables = [thenable]; + } else { + usedThenables[index] = thenable; } + suspendedThenable = thenable; // We use an expando to track the status and result of a thenable so that we @@ -105,34 +93,12 @@ export function trackSuspendedWakeable(wakeable: Wakeable) { export function resetWakeableStateAfterEachAttempt() { suspendedThenable = null; - adHocSuspendCount = 0; - lastUsedThenable = null; } export function resetThenableStateOnCompletion() { usedThenables = null; } -export function throwIfInfinitePingLoopDetected() { - if (adHocSuspendCount > MAX_AD_HOC_SUSPEND_COUNT) { - // TODO: Guard against an infinite loop by throwing an error if the same - // component suspends too many times in a row. This should be thrown from - // the render phase so that it gets the component stack. - } -} - -export function trackUsedThenable(thenable: Thenable, index: number) { - if (usedThenables === null) { - usedThenables = []; - } - usedThenables[index] = thenable; - lastUsedThenable = thenable; - - if (__DEV__ && ReactCurrentActQueue.current !== null) { - ReactCurrentActQueue.didUsePromise = true; - } -} - export function getPreviouslyUsedThenableAtIndex( index: number, ): Thenable | null { diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index cc2f1af4af59d..32229d31851cd 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -267,10 +267,9 @@ import {processTransitionCallbacks} from './ReactFiberTracingMarkerComponent.new import { resetWakeableStateAfterEachAttempt, resetThenableStateOnCompletion, - trackSuspendedWakeable, suspendedThenableDidResolve, isTrackingSuspendedThenable, -} from './ReactFiberWakeable.new'; +} from './ReactFiberThenable.new'; import {schedulePostPaintCallback} from './ReactPostPaintCallback'; const ceil = Math.ceil; @@ -1739,11 +1738,6 @@ function handleThrow(root, thrownValue): void { return; } - const isWakeable = - thrownValue !== null && - typeof thrownValue === 'object' && - typeof thrownValue.then === 'function'; - if (enableProfilerTimer && erroredWork.mode & ProfileMode) { // Record the time spent rendering before an error was thrown. This // avoids inaccurate Profiler durations in the case of a @@ -1753,7 +1747,11 @@ function handleThrow(root, thrownValue): void { if (enableSchedulingProfiler) { markComponentRenderStopped(); - if (isWakeable) { + if ( + thrownValue !== null && + typeof thrownValue === 'object' && + typeof thrownValue.then === 'function' + ) { const wakeable: Wakeable = (thrownValue: any); markComponentSuspended( erroredWork, @@ -1768,12 +1766,6 @@ function handleThrow(root, thrownValue): void { ); } } - - if (isWakeable) { - const wakeable: Wakeable = (thrownValue: any); - - trackSuspendedWakeable(wakeable); - } } function pushDispatcher(container) { diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 8c15823869d77..4c86b9707b452 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -267,10 +267,9 @@ import {processTransitionCallbacks} from './ReactFiberTracingMarkerComponent.old import { resetWakeableStateAfterEachAttempt, resetThenableStateOnCompletion, - trackSuspendedWakeable, suspendedThenableDidResolve, isTrackingSuspendedThenable, -} from './ReactFiberWakeable.old'; +} from './ReactFiberThenable.old'; import {schedulePostPaintCallback} from './ReactPostPaintCallback'; const ceil = Math.ceil; @@ -1739,11 +1738,6 @@ function handleThrow(root, thrownValue): void { return; } - const isWakeable = - thrownValue !== null && - typeof thrownValue === 'object' && - typeof thrownValue.then === 'function'; - if (enableProfilerTimer && erroredWork.mode & ProfileMode) { // Record the time spent rendering before an error was thrown. This // avoids inaccurate Profiler durations in the case of a @@ -1753,7 +1747,11 @@ function handleThrow(root, thrownValue): void { if (enableSchedulingProfiler) { markComponentRenderStopped(); - if (isWakeable) { + if ( + thrownValue !== null && + typeof thrownValue === 'object' && + typeof thrownValue.then === 'function' + ) { const wakeable: Wakeable = (thrownValue: any); markComponentSuspended( erroredWork, @@ -1768,12 +1766,6 @@ function handleThrow(root, thrownValue): void { ); } } - - if (isWakeable) { - const wakeable: Wakeable = (thrownValue: any); - - trackSuspendedWakeable(wakeable); - } } function pushDispatcher(container) { diff --git a/packages/react-reconciler/src/__tests__/ReactOffscreenSuspense-test.js b/packages/react-reconciler/src/__tests__/ReactOffscreenSuspense-test.js index 3c3a79a8efd74..e1a6ca49fd64c 100644 --- a/packages/react-reconciler/src/__tests__/ReactOffscreenSuspense-test.js +++ b/packages/react-reconciler/src/__tests__/ReactOffscreenSuspense-test.js @@ -485,32 +485,22 @@ describe('ReactOffscreen', () => { // In the same render, also hide the offscreen tree. root.render(); - if (gate(flags => flags.enableSyncDefaultUpdates)) { - expect(Scheduler).toFlushUntilNextPaint([ - // The outer update will commit, but the inner update is deferred until - // a later render. - 'Outer: 1', - - // Something suspended. This means we won't commit immediately; there - // will be an async gap between render and commit. In this test, we will - // use this property to schedule a concurrent update. The fact that - // we're using Suspense to schedule a concurrent update is not directly - // relevant to the test — we could also use time slicing, but I've - // chosen to use Suspense the because implementation details of time - // slicing are more volatile. - 'Suspend! [Async: 1]', - - 'Loading...', - ]); - } else { - // When default updates are time sliced, React yields before preparing - // the fallback. - expect(Scheduler).toFlushUntilNextPaint([ - 'Outer: 1', - 'Suspend! [Async: 1]', - ]); - expect(Scheduler).toFlushUntilNextPaint(['Loading...']); - } + expect(Scheduler).toFlushUntilNextPaint([ + // The outer update will commit, but the inner update is deferred until + // a later render. + 'Outer: 1', + + // Something suspended. This means we won't commit immediately; there + // will be an async gap between render and commit. In this test, we will + // use this property to schedule a concurrent update. The fact that + // we're using Suspense to schedule a concurrent update is not directly + // relevant to the test — we could also use time slicing, but I've + // chosen to use Suspense the because implementation details of time + // slicing are more volatile. + 'Suspend! [Async: 1]', + + 'Loading...', + ]); // Assert that we haven't committed quite yet expect(root).toMatchRenderedOutput( diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js index a0a7089157440..1c738f43dba11 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js @@ -3874,6 +3874,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { 'Suspend! [A2]', 'Loading...', 'Suspend! [B2]', + 'Loading...', ]); expect(root).toMatchRenderedOutput( <> diff --git a/packages/react-reconciler/src/__tests__/ReactWakeable-test.js b/packages/react-reconciler/src/__tests__/ReactThenable-test.js similarity index 97% rename from packages/react-reconciler/src/__tests__/ReactWakeable-test.js rename to packages/react-reconciler/src/__tests__/ReactThenable-test.js index bb15bc7a06862..5062ff71658ca 100644 --- a/packages/react-reconciler/src/__tests__/ReactWakeable-test.js +++ b/packages/react-reconciler/src/__tests__/ReactThenable-test.js @@ -26,6 +26,11 @@ describe('ReactWakeable', () => { return props.text; } + // This behavior was intentionally disabled to derisk the rollout of `use`. + // It changes the behavior of old, pre-`use` Suspense implementations. We may + // add this back; however, the plan is to migrate all existing Suspense code + // to `use`, so the extra code probably isn't worth it. + // @gate TODO test('if suspended fiber is pinged in a microtask, retry immediately without unwinding the stack', async () => { let resolved = false; function Async() { diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index f404a71c6ad5f..5545d8ef8d46e 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -21,7 +21,7 @@ import type { import type {ResponseState} from './ReactServerFormatConfig'; import type {Task} from './ReactFizzServer'; -import type {ThenableState} from './ReactFizzWakeable'; +import type {ThenableState} from './ReactFizzThenable'; import {readContext as readContextImpl} from './ReactFizzNewContext'; import {getTreeId} from './ReactFizzTreeContext'; @@ -29,7 +29,7 @@ import { getPreviouslyUsedThenableAtIndex, createThenableState, trackUsedThenable, -} from './ReactFizzWakeable'; +} from './ReactFizzThenable'; import {makeId} from './ReactServerFormatConfig'; @@ -593,6 +593,7 @@ function use(usable: Usable): T { const index = thenableIndexCounter; thenableIndexCounter += 1; + // TODO: Unify this switch statement with the one in trackUsedThenable. switch (thenable.status) { case 'fulfilled': { const fulfilledValue: T = thenable.value; diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index f6dc21abcfeb8..aaf0fc2f5b33c 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -17,7 +17,6 @@ import type { ReactContext, ReactProviderType, OffscreenMode, - Wakeable, } from 'shared/ReactTypes'; import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type { @@ -30,7 +29,7 @@ import type { import type {ContextSnapshot} from './ReactFizzNewContext'; import type {ComponentStackNode} from './ReactFizzComponentStack'; import type {TreeContext} from './ReactFizzTreeContext'; -import type {ThenableState} from './ReactFizzWakeable'; +import type {ThenableState} from './ReactFizzThenable'; import { scheduleWork, @@ -139,7 +138,6 @@ import { import assign from 'shared/assign'; import getComponentNameFromType from 'shared/getComponentNameFromType'; import isArray from 'shared/isArray'; -import {trackSuspendedWakeable} from './ReactFizzWakeable'; const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; const ReactCurrentCache = ReactSharedInternals.ReactCurrentCache; @@ -1554,8 +1552,6 @@ function spawnNewSuspendedTask( task.treeContext, ); - trackSuspendedWakeable(x); - if (__DEV__) { if (task.componentStack !== null) { // We pop one task off the stack because the node that suspended will be tried again, @@ -1879,9 +1875,6 @@ function retryTask(request: Request, task: Task): void { // Something suspended again, let's pick it back up later. const ping = task.ping; x.then(ping, ping); - - const wakeable: Wakeable = x; - trackSuspendedWakeable(wakeable); task.thenableState = getThenableStateAfterSuspending(); } else { task.abortSet.delete(task); diff --git a/packages/react-server/src/ReactFizzWakeable.js b/packages/react-server/src/ReactFizzThenable.js similarity index 87% rename from packages/react-server/src/ReactFizzWakeable.js rename to packages/react-server/src/ReactFizzThenable.js index 56066a9cc24c2..52cedc579e3f0 100644 --- a/packages/react-server/src/ReactFizzWakeable.js +++ b/packages/react-server/src/ReactFizzThenable.js @@ -14,7 +14,6 @@ // instead of "Wakeable". Or some other more appropriate name. import type { - Wakeable, Thenable, PendingThenable, FulfilledThenable, @@ -30,12 +29,12 @@ export function createThenableState(): ThenableState { return []; } -export function trackSuspendedWakeable(wakeable: Wakeable) { - // If this wakeable isn't already a thenable, turn it into one now. Then, - // when we resume the work loop, we can check if its status is - // still pending. - // TODO: Get rid of the Wakeable type? It's superseded by UntrackedThenable. - const thenable: Thenable = (wakeable: any); +export function trackUsedThenable( + thenableState: ThenableState, + thenable: Thenable, + index: number, +) { + thenableState[index] = thenable; // We use an expando to track the status and result of a thenable so that we // can synchronously unwrap the value. Think of this as an extension of the @@ -82,16 +81,6 @@ export function trackSuspendedWakeable(wakeable: Wakeable) { } } -export function trackUsedThenable( - thenableState: ThenableState, - thenable: Thenable, - index: number, -) { - // This is only a separate function from trackSuspendedWakeable for symmetry - // with Fiber. - thenableState[index] = thenable; -} - export function getPreviouslyUsedThenableAtIndex( thenableState: ThenableState | null, index: number, diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js index 1547f29dbcfbe..0f7f5b17e9a68 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -10,7 +10,7 @@ import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes'; import type {Request} from './ReactFlightServer'; import type {ReactServerContext, Thenable, Usable} from 'shared/ReactTypes'; -import type {ThenableState} from './ReactFlightWakeable'; +import type {ThenableState} from './ReactFlightThenable'; import { REACT_SERVER_CONTEXT_TYPE, REACT_MEMO_CACHE_SENTINEL, @@ -21,7 +21,7 @@ import { getPreviouslyUsedThenableAtIndex, createThenableState, trackUsedThenable, -} from './ReactFlightWakeable'; +} from './ReactFlightThenable'; let currentRequest = null; let thenableIndexCounter = 0; diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 3a9849a19ed49..ff19ea3abc839 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -16,7 +16,7 @@ import type { ModuleKey, } from './ReactFlightServerConfig'; import type {ContextSnapshot} from './ReactFlightNewContext'; -import type {ThenableState} from './ReactFlightWakeable'; +import type {ThenableState} from './ReactFlightThenable'; import type { ReactProviderType, ServerContextJSONValue, @@ -64,7 +64,7 @@ import { getActiveContext, rootContextSnapshot, } from './ReactFlightNewContext'; -import {trackSuspendedWakeable} from './ReactFlightWakeable'; +import {trackSuspendedWakeable} from './ReactFlightThenable'; import { REACT_ELEMENT_TYPE, diff --git a/packages/react-server/src/ReactFlightWakeable.js b/packages/react-server/src/ReactFlightThenable.js similarity index 97% rename from packages/react-server/src/ReactFlightWakeable.js rename to packages/react-server/src/ReactFlightThenable.js index 7c344d1d01126..ac47b76bc1acb 100644 --- a/packages/react-server/src/ReactFlightWakeable.js +++ b/packages/react-server/src/ReactFlightThenable.js @@ -30,6 +30,8 @@ export function createThenableState(): ThenableState { return []; } +// TODO: Unify this with trackSuspendedThenable. It needs to support not only +// `use`, but async components, too. export function trackSuspendedWakeable(wakeable: Wakeable) { // If this wakeable isn't already a thenable, turn it into one now. Then, // when we resume the work loop, we can check if its status is diff --git a/scripts/jest/TestFlags.js b/scripts/jest/TestFlags.js index ee093653182ac..ed792b948f23e 100644 --- a/scripts/jest/TestFlags.js +++ b/scripts/jest/TestFlags.js @@ -48,6 +48,7 @@ const environmentFlags = { // Use this for tests that are known to be broken. FIXME: false, + TODO: false, // Turn these flags back on (or delete) once the effect list is removed in // favor of a depth-first traversal using `subtreeTags`.