diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
index 82d103cfecf89..1923cdbca0baa 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
@@ -19,7 +19,6 @@ let Suspense;
let SuspenseList;
let useSyncExternalStore;
let useSyncExternalStoreWithSelector;
-let use;
let PropTypes;
let textCache;
let window;
@@ -43,7 +42,6 @@ describe('ReactDOMFizzServer', () => {
Suspense = React.Suspense;
if (gate(flags => flags.enableSuspenseList)) {
SuspenseList = React.SuspenseList;
- use = React.experimental_use;
}
PropTypes = require('prop-types');
@@ -5015,264 +5013,6 @@ describe('ReactDOMFizzServer', () => {
console.error = originalConsoleError;
}
});
-
- // @gate enableUseHook
- it('basic use(promise)', async () => {
- const promiseA = Promise.resolve('A');
- const promiseB = Promise.resolve('B');
- const promiseC = Promise.resolve('C');
-
- function Async() {
- return use(promiseA) + use(promiseB) + use(promiseC);
- }
-
- function App() {
- return (
-
-
-
- );
- }
-
- await act(async () => {
- const {pipe} = ReactDOMFizzServer.renderToPipeableStream();
- pipe(writable);
- });
-
- // TODO: The `act` implementation in this file doesn't unwrap microtasks
- // automatically. We can't use the same `act` we use for Fiber tests
- // because that relies on the mock Scheduler. Doesn't affect any public
- // API but we might want to fix this for our own internal tests.
- //
- // For now, wait for each promise in sequence.
- await act(async () => {
- await promiseA;
- });
- await act(async () => {
- await promiseB;
- });
- await act(async () => {
- await promiseC;
- });
-
- expect(getVisibleChildren(container)).toEqual('ABC');
-
- ReactDOMClient.hydrateRoot(container, );
- expect(Scheduler).toFlushAndYield([]);
- expect(getVisibleChildren(container)).toEqual('ABC');
- });
-
- // @gate enableUseHook
- it('basic use(context)', async () => {
- const ContextA = React.createContext('default');
- const ContextB = React.createContext('B');
- const ServerContext = React.createServerContext(
- 'ServerContext',
- 'default',
- );
- function Client() {
- return use(ContextA) + use(ContextB);
- }
- function ServerComponent() {
- return use(ServerContext);
- }
- function Server() {
- return (
-
-
-
- );
- }
- function App() {
- return (
- <>
-
-
-
-
- >
- );
- }
-
- await act(async () => {
- const {pipe} = ReactDOMFizzServer.renderToPipeableStream();
- pipe(writable);
- });
- expect(getVisibleChildren(container)).toEqual(['AB', 'C']);
-
- // Hydration uses a different renderer runtime (Fiber instead of Fizz).
- // We reset _currentRenderer here to not trigger a warning about multiple
- // renderers concurrently using these contexts
- ContextA._currentRenderer = null;
- ServerContext._currentRenderer = null;
- ReactDOMClient.hydrateRoot(container, );
- expect(Scheduler).toFlushAndYield([]);
- expect(getVisibleChildren(container)).toEqual(['AB', 'C']);
- });
-
- // @gate enableUseHook
- it('use(promise) in multiple components', async () => {
- const promiseA = Promise.resolve('A');
- const promiseB = Promise.resolve('B');
- const promiseC = Promise.resolve('C');
- const promiseD = Promise.resolve('D');
-
- function Child({prefix}) {
- return prefix + use(promiseC) + use(promiseD);
- }
-
- function Parent() {
- return ;
- }
-
- function App() {
- return (
-
-
-
- );
- }
-
- await act(async () => {
- const {pipe} = ReactDOMFizzServer.renderToPipeableStream();
- pipe(writable);
- });
-
- // TODO: The `act` implementation in this file doesn't unwrap microtasks
- // automatically. We can't use the same `act` we use for Fiber tests
- // because that relies on the mock Scheduler. Doesn't affect any public
- // API but we might want to fix this for our own internal tests.
- //
- // For now, wait for each promise in sequence.
- await act(async () => {
- await promiseA;
- });
- await act(async () => {
- await promiseB;
- });
- await act(async () => {
- await promiseC;
- });
- await act(async () => {
- await promiseD;
- });
-
- expect(getVisibleChildren(container)).toEqual('ABCD');
-
- ReactDOMClient.hydrateRoot(container, );
- expect(Scheduler).toFlushAndYield([]);
- expect(getVisibleChildren(container)).toEqual('ABCD');
- });
-
- // @gate enableUseHook
- it('using a rejected promise will throw', async () => {
- const promiseA = Promise.resolve('A');
- const promiseB = Promise.reject(new Error('Oops!'));
- const promiseC = Promise.resolve('C');
-
- // Jest/Node will raise an unhandled rejected error unless we await this. It
- // works fine in the browser, though.
- await expect(promiseB).rejects.toThrow('Oops!');
-
- function Async() {
- return use(promiseA) + use(promiseB) + use(promiseC);
- }
-
- class ErrorBoundary extends React.Component {
- state = {error: null};
- static getDerivedStateFromError(error) {
- return {error};
- }
- render() {
- if (this.state.error) {
- return this.state.error.message;
- }
- return this.props.children;
- }
- }
-
- function App() {
- return (
-
-
-
-
-
- );
- }
-
- const reportedServerErrors = [];
- await act(async () => {
- const {pipe} = ReactDOMFizzServer.renderToPipeableStream(, {
- onError(error) {
- reportedServerErrors.push(error);
- },
- });
- pipe(writable);
- });
-
- // TODO: The `act` implementation in this file doesn't unwrap microtasks
- // automatically. We can't use the same `act` we use for Fiber tests
- // because that relies on the mock Scheduler. Doesn't affect any public
- // API but we might want to fix this for our own internal tests.
- //
- // For now, wait for each promise in sequence.
- await act(async () => {
- await promiseA;
- });
- await act(async () => {
- await expect(promiseB).rejects.toThrow('Oops!');
- });
- await act(async () => {
- await promiseC;
- });
-
- expect(getVisibleChildren(container)).toEqual('Loading...');
- expect(reportedServerErrors.length).toBe(1);
- expect(reportedServerErrors[0].message).toBe('Oops!');
-
- const reportedClientErrors = [];
- ReactDOMClient.hydrateRoot(container, , {
- onRecoverableError(error) {
- reportedClientErrors.push(error);
- },
- });
- expect(Scheduler).toFlushAndYield([]);
- expect(getVisibleChildren(container)).toEqual('Oops!');
- expect(reportedClientErrors.length).toBe(1);
- if (__DEV__) {
- expect(reportedClientErrors[0].message).toBe('Oops!');
- } else {
- expect(reportedClientErrors[0].message).toBe(
- 'The server could not finish this Suspense boundary, likely due to ' +
- 'an error during server rendering. Switched to client rendering.',
- );
- }
- });
-
- // @gate enableUseHook
- it("use a promise that's already been instrumented and resolved", async () => {
- const thenable = {
- status: 'fulfilled',
- value: 'Hi',
- then() {},
- };
-
- // This will never suspend because the thenable already resolved
- function App() {
- return use(thenable);
- }
-
- await act(async () => {
- const {pipe} = ReactDOMFizzServer.renderToPipeableStream();
- pipe(writable);
- });
- expect(getVisibleChildren(container)).toEqual('Hi');
-
- ReactDOMClient.hydrateRoot(container, );
- expect(Scheduler).toFlushAndYield([]);
- expect(getVisibleChildren(container)).toEqual('Hi');
- });
});
describe('useEvent', () => {
diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js
index 8db3f319e3cc1..19ed6bb96f742 100644
--- a/packages/react-server/src/ReactFizzHooks.js
+++ b/packages/react-server/src/ReactFizzHooks.js
@@ -18,35 +18,22 @@ import type {
MutableSourceSubscribeFn,
ReactContext,
StartTransitionOptions,
- Thenable,
- Usable,
} from 'shared/ReactTypes';
import type {ResponseState} from './ReactServerFormatConfig';
import type {Task} from './ReactFizzServer';
-import type {ThenableState} from './ReactFizzWakeable';
import {readContext as readContextImpl} from './ReactFizzNewContext';
import {getTreeId} from './ReactFizzTreeContext';
-import {
- getPreviouslyUsedThenableAtIndex,
- createThenableState,
- trackUsedThenable,
-} from './ReactFizzWakeable';
import {makeId} from './ReactServerFormatConfig';
import {
enableCache,
- enableUseHook,
enableUseEventHook,
enableUseMemoCacheHook,
} from 'shared/ReactFeatureFlags';
import is from 'shared/objectIs';
-import {
- REACT_SERVER_CONTEXT_TYPE,
- REACT_CONTEXT_TYPE,
-} from 'shared/ReactSymbols';
type BasicStateAction = (S => S) | S;
type Dispatch = A => void;
@@ -77,9 +64,6 @@ let isReRender: boolean = false;
let didScheduleRenderPhaseUpdate: boolean = false;
// Counts the number of useId hooks in this component
let localIdCounter: number = 0;
-// Counts the number of use(thenable) calls in this component
-let thenableIndexCounter: number = 0;
-let thenableState: ThenableState | null = null;
// Lazily created map of render-phase updates
let renderPhaseUpdates: Map, Update> | null = null;
// Counter to prevent infinite loops.
@@ -194,11 +178,7 @@ function createWorkInProgressHook(): Hook {
return workInProgressHook;
}
-export function prepareToUseHooks(
- task: Task,
- componentIdentity: Object,
- prevThenableState: ThenableState | null,
-): void {
+export function prepareToUseHooks(task: Task, componentIdentity: Object): void {
currentlyRenderingComponent = componentIdentity;
currentlyRenderingTask = task;
if (__DEV__) {
@@ -207,14 +187,13 @@ export function prepareToUseHooks(
// The following should have already been reset
// didScheduleRenderPhaseUpdate = false;
+ // localIdCounter = 0;
// firstWorkInProgressHook = null;
// numberOfReRenders = 0;
// renderPhaseUpdates = null;
// workInProgressHook = null;
localIdCounter = 0;
- thenableIndexCounter = 0;
- thenableState = prevThenableState;
}
export function finishHooks(
@@ -233,7 +212,6 @@ export function finishHooks(
// restarting until no more updates are scheduled.
didScheduleRenderPhaseUpdate = false;
localIdCounter = 0;
- thenableIndexCounter = 0;
numberOfReRenders += 1;
// Start over from the beginning of the list
@@ -245,13 +223,7 @@ export function finishHooks(
return children;
}
-export function getThenableStateAfterSuspending(): null | ThenableState {
- const state = thenableState;
- thenableState = null;
- return state;
-}
-
-export function checkDidRenderIdHook(): boolean {
+export function checkDidRenderIdHook() {
// This should be called immediately after every finishHooks call.
// Conceptually, it's part of the return value of finishHooks; it's only a
// separate function to avoid using an array tuple.
@@ -589,80 +561,6 @@ function useId(): string {
return makeId(responseState, treeId, localId);
}
-function use(usable: Usable): T {
- if (usable !== null && typeof usable === 'object') {
- // $FlowFixMe[method-unbinding]
- if (typeof usable.then === 'function') {
- // This is a thenable.
- const thenable: Thenable = (usable: any);
-
- // Track the position of the thenable within this fiber.
- const index = thenableIndexCounter;
- thenableIndexCounter += 1;
-
- switch (thenable.status) {
- case 'fulfilled': {
- const fulfilledValue: T = thenable.value;
- return fulfilledValue;
- }
- case 'rejected': {
- const rejectedError = thenable.reason;
- throw rejectedError;
- }
- default: {
- const prevThenableAtIndex: Thenable | null = getPreviouslyUsedThenableAtIndex(
- thenableState,
- index,
- );
- if (prevThenableAtIndex !== null) {
- switch (prevThenableAtIndex.status) {
- case 'fulfilled': {
- const fulfilledValue: T = prevThenableAtIndex.value;
- return fulfilledValue;
- }
- case 'rejected': {
- const rejectedError: mixed = prevThenableAtIndex.reason;
- throw rejectedError;
- }
- default: {
- // The thenable still hasn't resolved. Suspend with the same
- // thenable as last time to avoid redundant listeners.
- throw prevThenableAtIndex;
- }
- }
- } else {
- // This is the first time something has been used at this index.
- // Stash the thenable at the current index so we can reuse it during
- // the next attempt.
- if (thenableState === null) {
- thenableState = createThenableState();
- }
- trackUsedThenable(thenableState, thenable, index);
-
- // Suspend.
- // TODO: Throwing here is an implementation detail that allows us to
- // unwind the call stack. But we shouldn't allow it to leak into
- // userspace. Throw an opaque placeholder value instead of the
- // actual thenable. If it doesn't get captured by the work loop, log
- // a warning, because that means something in userspace must have
- // caught it.
- throw thenable;
- }
- }
- }
- } else if (
- usable.$$typeof === REACT_CONTEXT_TYPE ||
- usable.$$typeof === REACT_SERVER_CONTEXT_TYPE
- ) {
- const context: ReactContext = (usable: any);
- return readContext(context);
- }
- }
-
- // eslint-disable-next-line react-internal/safe-string-coercion
- throw new Error('An unsupported type was passed to use(): ' + String(usable));
-}
-
function unsupportedRefresh() {
throw new Error('Cache cannot be refreshed during server rendering.');
}
@@ -711,9 +609,6 @@ if (enableUseEventHook) {
if (enableUseMemoCacheHook) {
Dispatcher.useMemoCache = useMemoCache;
}
-if (enableUseHook) {
- Dispatcher.use = use;
-}
export let currentResponseState: null | ResponseState = (null: any);
export function setCurrentResponseState(
diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js
index 0da6a37ca47da..8ecbe149aaf42 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,6 @@ import type {
import type {ContextSnapshot} from './ReactFizzNewContext';
import type {ComponentStackNode} from './ReactFizzComponentStack';
import type {TreeContext} from './ReactFizzTreeContext';
-import type {ThenableState} from './ReactFizzWakeable';
import {
scheduleWork,
@@ -100,7 +98,6 @@ import {
Dispatcher,
currentResponseState,
setCurrentResponseState,
- getThenableStateAfterSuspending,
} from './ReactFizzHooks';
import {getStackByComponentStackNode} from './ReactFizzComponentStack';
import {emptyTreeContext, pushTreeContext} from './ReactFizzTreeContext';
@@ -137,7 +134,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 ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
@@ -171,7 +167,6 @@ export type Task = {
context: ContextSnapshot, // the current new context that this task is executing in
treeContext: TreeContext, // the current tree context that this task is executing in
componentStack: null | ComponentStackNode, // DEV-only component stack
- thenableState: null | ThenableState,
};
const PENDING = 0;
@@ -317,7 +312,6 @@ export function createRequest(
rootSegment.parentFlushed = true;
const rootTask = createTask(
request,
- null,
children,
null,
rootSegment,
@@ -358,7 +352,6 @@ function createSuspenseBoundary(
function createTask(
request: Request,
- thenableState: ThenableState | null,
node: ReactNodeList,
blockedBoundary: Root | SuspenseBoundary,
blockedSegment: Segment,
@@ -382,7 +375,6 @@ function createTask(
legacyContext,
context,
treeContext,
- thenableState,
}: any);
if (__DEV__) {
task.componentStack = null;
@@ -633,7 +625,6 @@ function renderSuspenseBoundary(
// on it yet in case we finish the main content, so we queue for later.
const suspendedFallbackTask = createTask(
request,
- null,
fallback,
parentBoundary,
boundarySegment,
@@ -723,13 +714,12 @@ function shouldConstruct(Component) {
function renderWithHooks(
request: Request,
task: Task,
- prevThenableState: ThenableState | null,
Component: (p: Props, arg: SecondArg) => any,
props: Props,
secondArg: SecondArg,
): any {
const componentIdentity = {};
- prepareToUseHooks(task, componentIdentity, prevThenableState);
+ prepareToUseHooks(task, componentIdentity);
const result = Component(props, secondArg);
return finishHooks(Component, props, result, secondArg);
}
@@ -767,13 +757,13 @@ function finishClassComponent(
childContextTypes,
);
task.legacyContext = mergedContext;
- renderNodeDestructive(request, task, null, nextChildren);
+ renderNodeDestructive(request, task, nextChildren);
task.legacyContext = previousContext;
return;
}
}
- renderNodeDestructive(request, task, null, nextChildren);
+ renderNodeDestructive(request, task, nextChildren);
}
function renderClassComponent(
@@ -807,7 +797,6 @@ let hasWarnedAboutUsingContextAsConsumer = false;
function renderIndeterminateComponent(
request: Request,
task: Task,
- prevThenableState: ThenableState | null,
Component: any,
props: any,
): void {
@@ -836,14 +825,7 @@ function renderIndeterminateComponent(
}
}
- const value = renderWithHooks(
- request,
- task,
- prevThenableState,
- Component,
- props,
- legacyContext,
- );
+ const value = renderWithHooks(request, task, Component, props, legacyContext);
const hasId = checkDidRenderIdHook();
if (__DEV__) {
@@ -924,12 +906,12 @@ function renderIndeterminateComponent(
const index = 0;
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
try {
- renderNodeDestructive(request, task, null, value);
+ renderNodeDestructive(request, task, value);
} finally {
task.treeContext = prevTreeContext;
}
} else {
- renderNodeDestructive(request, task, null, value);
+ renderNodeDestructive(request, task, value);
}
}
popComponentStackInDEV(task);
@@ -1009,20 +991,12 @@ function resolveDefaultProps(Component: any, baseProps: Object): Object {
function renderForwardRef(
request: Request,
task: Task,
- prevThenableState,
type: any,
props: Object,
ref: any,
): void {
pushFunctionComponentStackInDEV(task, type.render);
- const children = renderWithHooks(
- request,
- task,
- prevThenableState,
- type.render,
- props,
- ref,
- );
+ const children = renderWithHooks(request, task, type.render, props, ref);
const hasId = checkDidRenderIdHook();
if (hasId) {
// This component materialized an id. We treat this as its own level, with
@@ -1032,12 +1006,12 @@ function renderForwardRef(
const index = 0;
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
try {
- renderNodeDestructive(request, task, null, children);
+ renderNodeDestructive(request, task, children);
} finally {
task.treeContext = prevTreeContext;
}
} else {
- renderNodeDestructive(request, task, null, children);
+ renderNodeDestructive(request, task, children);
}
popComponentStackInDEV(task);
}
@@ -1045,21 +1019,13 @@ function renderForwardRef(
function renderMemo(
request: Request,
task: Task,
- prevThenableState: ThenableState | null,
type: any,
props: Object,
ref: any,
): void {
const innerType = type.type;
const resolvedProps = resolveDefaultProps(innerType, props);
- renderElement(
- request,
- task,
- prevThenableState,
- innerType,
- resolvedProps,
- ref,
- );
+ renderElement(request, task, innerType, resolvedProps, ref);
}
function renderContextConsumer(
@@ -1109,7 +1075,7 @@ function renderContextConsumer(
const newValue = readContext(context);
const newChildren = render(newValue);
- renderNodeDestructive(request, task, null, newChildren);
+ renderNodeDestructive(request, task, newChildren);
}
function renderContextProvider(
@@ -1126,7 +1092,7 @@ function renderContextProvider(
prevSnapshot = task.context;
}
task.context = pushProvider(context, value);
- renderNodeDestructive(request, task, null, children);
+ renderNodeDestructive(request, task, children);
task.context = popProvider(context);
if (__DEV__) {
if (prevSnapshot !== task.context) {
@@ -1140,7 +1106,6 @@ function renderContextProvider(
function renderLazyComponent(
request: Request,
task: Task,
- prevThenableState: ThenableState | null,
lazyComponent: LazyComponentType,
props: Object,
ref: any,
@@ -1150,14 +1115,7 @@ function renderLazyComponent(
const init = lazyComponent._init;
const Component = init(payload);
const resolvedProps = resolveDefaultProps(Component, props);
- renderElement(
- request,
- task,
- prevThenableState,
- Component,
- resolvedProps,
- ref,
- );
+ renderElement(request, task, Component, resolvedProps, ref);
popComponentStackInDEV(task);
}
@@ -1169,14 +1127,13 @@ function renderOffscreen(request: Request, task: Task, props: Object): void {
} else {
// A visible Offscreen boundary is treated exactly like a fragment: a
// pure indirection.
- renderNodeDestructive(request, task, null, props.children);
+ renderNodeDestructive(request, task, props.children);
}
}
function renderElement(
request: Request,
task: Task,
- prevThenableState: ThenableState | null,
type: any,
props: Object,
ref: any,
@@ -1186,13 +1143,7 @@ function renderElement(
renderClassComponent(request, task, type, props);
return;
} else {
- renderIndeterminateComponent(
- request,
- task,
- prevThenableState,
- type,
- props,
- );
+ renderIndeterminateComponent(request, task, type, props);
return;
}
}
@@ -1216,7 +1167,7 @@ function renderElement(
case REACT_STRICT_MODE_TYPE:
case REACT_PROFILER_TYPE:
case REACT_FRAGMENT_TYPE: {
- renderNodeDestructive(request, task, null, props.children);
+ renderNodeDestructive(request, task, props.children);
return;
}
case REACT_OFFSCREEN_TYPE: {
@@ -1226,13 +1177,13 @@ function renderElement(
case REACT_SUSPENSE_LIST_TYPE: {
pushBuiltInComponentStackInDEV(task, 'SuspenseList');
// TODO: SuspenseList should control the boundaries.
- renderNodeDestructive(request, task, null, props.children);
+ renderNodeDestructive(request, task, props.children);
popComponentStackInDEV(task);
return;
}
case REACT_SCOPE_TYPE: {
if (enableScopeAPI) {
- renderNodeDestructive(request, task, null, props.children);
+ renderNodeDestructive(request, task, props.children);
return;
}
throw new Error('ReactDOMServer does not yet support scope components.');
@@ -1254,11 +1205,11 @@ function renderElement(
if (typeof type === 'object' && type !== null) {
switch (type.$$typeof) {
case REACT_FORWARD_REF_TYPE: {
- renderForwardRef(request, task, prevThenableState, type, props, ref);
+ renderForwardRef(request, task, type, props, ref);
return;
}
case REACT_MEMO_TYPE: {
- renderMemo(request, task, prevThenableState, type, props, ref);
+ renderMemo(request, task, type, props, ref);
return;
}
case REACT_PROVIDER_TYPE: {
@@ -1270,7 +1221,7 @@ function renderElement(
return;
}
case REACT_LAZY_TYPE: {
- renderLazyComponent(request, task, prevThenableState, type, props);
+ renderLazyComponent(request, task, type, props);
return;
}
}
@@ -1335,9 +1286,6 @@ function validateIterable(iterable, iteratorFn: Function): void {
function renderNodeDestructive(
request: Request,
task: Task,
- // The thenable state reused from the previous attempt, if any. This is almost
- // always null, except when called by retryTask.
- prevThenableState: ThenableState | null,
node: ReactNodeList,
): void {
if (__DEV__) {
@@ -1345,7 +1293,7 @@ function renderNodeDestructive(
// a component stack at the right place in the tree. We don't do this in renderNode
// becuase it is not called at every layer of the tree and we may lose frames
try {
- return renderNodeDestructiveImpl(request, task, prevThenableState, node);
+ return renderNodeDestructiveImpl(request, task, node);
} catch (x) {
if (typeof x === 'object' && x !== null && typeof x.then === 'function') {
// This is a Wakable, noop
@@ -1360,7 +1308,7 @@ function renderNodeDestructive(
throw x;
}
} else {
- return renderNodeDestructiveImpl(request, task, prevThenableState, node);
+ return renderNodeDestructiveImpl(request, task, node);
}
}
@@ -1369,7 +1317,6 @@ function renderNodeDestructive(
function renderNodeDestructiveImpl(
request: Request,
task: Task,
- prevThenableState: ThenableState | null,
node: ReactNodeList,
): void {
// Stash the node we're working on. We'll pick up from this task in case
@@ -1384,7 +1331,7 @@ function renderNodeDestructiveImpl(
const type = element.type;
const props = element.props;
const ref = element.ref;
- renderElement(request, task, prevThenableState, type, props, ref);
+ renderElement(request, task, type, props, ref);
return;
}
case REACT_PORTAL_TYPE:
@@ -1418,7 +1365,7 @@ function renderNodeDestructiveImpl(
} else {
resolvedNode = init(payload);
}
- renderNodeDestructive(request, task, null, resolvedNode);
+ renderNodeDestructive(request, task, resolvedNode);
return;
}
}
@@ -1520,7 +1467,6 @@ function renderChildrenArray(request, task, children) {
function spawnNewSuspendedTask(
request: Request,
task: Task,
- thenableState: ThenableState | null,
x: Promise,
): void {
// Something suspended, we'll need to create a new segment and resolve it later.
@@ -1541,7 +1487,6 @@ function spawnNewSuspendedTask(
segment.lastPushedText = false;
const newTask = createTask(
request,
- thenableState,
task.node,
task.blockedBoundary,
newSegment,
@@ -1550,9 +1495,6 @@ function spawnNewSuspendedTask(
task.context,
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,
@@ -1580,13 +1522,11 @@ function renderNode(request: Request, task: Task, node: ReactNodeList): void {
previousComponentStack = task.componentStack;
}
try {
- return renderNodeDestructive(request, task, null, node);
+ return renderNodeDestructive(request, task, node);
} catch (x) {
resetHooksState();
if (typeof x === 'object' && x !== null && typeof x.then === 'function') {
- const thenableState = getThenableStateAfterSuspending();
- spawnNewSuspendedTask(request, task, thenableState, x);
-
+ spawnNewSuspendedTask(request, task, x);
// Restore the context. We assume that this will be restored by the inner
// functions in case nothing throws so we don't use "finally" here.
task.blockedSegment.formatContext = previousFormatContext;
@@ -1852,14 +1792,7 @@ function retryTask(request: Request, task: Task): void {
try {
// We call the destructive form that mutates this task. That way if something
// suspends again, we can reuse the same task instead of spawning a new one.
-
- // Reset the task's thenable state before continuing, so that if a later
- // component suspends we can reuse the same task object. If the same
- // component suspends again, the thenable state will be restored.
- const prevThenableState = task.thenableState;
- task.thenableState = null;
-
- renderNodeDestructive(request, task, prevThenableState, task.node);
+ renderNodeDestructive(request, task, task.node);
pushSegmentFinale(
segment.chunks,
request.responseState,
@@ -1876,10 +1809,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);
segment.status = ERRORED;
diff --git a/packages/react-server/src/ReactFizzWakeable.js b/packages/react-server/src/ReactFizzWakeable.js
index aea0b507c8ca7..e69de29bb2d1d 100644
--- a/packages/react-server/src/ReactFizzWakeable.js
+++ b/packages/react-server/src/ReactFizzWakeable.js
@@ -1,106 +0,0 @@
-/**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @flow
- */
-
-// Corresponds to ReactFiberWakeable and ReactFlightWakeable modules. Generally,
-// changes to one module should be reflected in the others.
-
-// TODO: Rename this module and the corresponding Fiber one to "Thenable"
-// instead of "Wakeable". Or some other more appropriate name.
-
-import type {
- Wakeable,
- Thenable,
- PendingThenable,
- FulfilledThenable,
- RejectedThenable,
-} from 'shared/ReactTypes';
-
-// TODO: Sparse arrays are bad for performance.
-export opaque type ThenableState = Array | void>;
-
-export function createThenableState(): ThenableState {
- // The ThenableState is created the first time a component suspends. If it
- // suspends again, we'll reuse the same state.
- 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);
-
- // 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
- // Promise API, or a custom interface that is a superset of Thenable.
- //
- // If the thenable doesn't have a status, set it to "pending" and attach
- // a listener that will update its status and result when it resolves.
- switch (thenable.status) {
- case 'fulfilled':
- case 'rejected':
- // A thenable that already resolved shouldn't have been thrown, so this is
- // unexpected. Suggests a mistake in a userspace data library. Don't track
- // this thenable, because if we keep trying it will likely infinite loop
- // without ever resolving.
- // TODO: Log a warning?
- break;
- default: {
- if (typeof thenable.status === 'string') {
- // Only instrument the thenable if the status if not defined. If
- // it's defined, but an unknown value, assume it's been instrumented by
- // some custom userspace implementation. We treat it as "pending".
- break;
- }
- const pendingThenable: PendingThenable = (thenable: any);
- pendingThenable.status = 'pending';
- pendingThenable.then(
- fulfilledValue => {
- if (thenable.status === 'pending') {
- const fulfilledThenable: FulfilledThenable = (thenable: any);
- fulfilledThenable.status = 'fulfilled';
- fulfilledThenable.value = fulfilledValue;
- }
- },
- (error: mixed) => {
- if (thenable.status === 'pending') {
- const rejectedThenable: RejectedThenable = (thenable: any);
- rejectedThenable.status = 'rejected';
- rejectedThenable.reason = error;
- }
- },
- );
- break;
- }
- }
-}
-
-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,
-): Thenable | null {
- if (thenableState !== null) {
- const thenable = thenableState[index];
- if (thenable !== undefined) {
- return thenable;
- }
- }
- return null;
-}
diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js
index 07de6602e6288..941621b52f0d6 100644
--- a/packages/react-server/src/ReactFlightHooks.js
+++ b/packages/react-server/src/ReactFlightHooks.js
@@ -39,10 +39,8 @@ export function prepareToUseHooksForComponent(
thenableState = prevThenableState;
}
-export function getThenableStateAfterSuspending(): null | ThenableState {
- const state = thenableState;
- thenableState = null;
- return state;
+export function getThenableStateAfterSuspending() {
+ return thenableState;
}
function readContext(context: ReactServerContext): T {
@@ -204,9 +202,8 @@ function use(usable: Usable): T {
}
}
}
- } else if (usable.$$typeof === REACT_SERVER_CONTEXT_TYPE) {
- const context: ReactServerContext = (usable: any);
- return readContext(context);
+ } else {
+ // TODO: Add support for Context
}
}
diff --git a/packages/react-server/src/ReactFlightWakeable.js b/packages/react-server/src/ReactFlightWakeable.js
index b1ff2aa5eb3ef..9acacfbbddcc9 100644
--- a/packages/react-server/src/ReactFlightWakeable.js
+++ b/packages/react-server/src/ReactFlightWakeable.js
@@ -7,8 +7,8 @@
* @flow
*/
-// Corresponds to ReactFiberWakeable and ReactFizzWakeable modules. Generally,
-// changes to one module should be reflected in the others.
+// Corresponds to ReactFiberWakeable module. Generally, changes to one module
+// should be reflected in the other.
// TODO: Rename this module and the corresponding Fiber one to "Thenable"
// instead of "Wakeable". Or some other more appropriate name.