Refactor Lazy Components to use teh Suspense (and wrap Blocks in Lazy) (#18362)

* Refactor Lazy Components

* Switch Blocks to using a Lazy component wrapper

Then resolve to a true Block inside.

* Test component names of lazy Blocks
This commit is contained in:
Sebastian Markbåge 2020-03-22 21:53:05 -07:00 committed by GitHub
parent 31a9e391f7
commit fd61f7ea53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 296 additions and 347 deletions

View File

@ -709,7 +709,7 @@ describe('ReactDOMServer', () => {
),
);
ReactDOMServer.renderToString(<LazyFoo />);
}).toThrow('ReactDOMServer does not yet support lazy-loaded components.');
}).toThrow('ReactDOMServer does not yet support Suspense.');
});
it('throws when suspending on the server', () => {

View File

@ -17,8 +17,6 @@ import invariant from 'shared/invariant';
import getComponentName from 'shared/getComponentName';
import describeComponentFrame from 'shared/describeComponentFrame';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import {initializeLazyComponentType} from 'shared/ReactLazyComponent';
import {Resolved, Rejected, Pending} from 'shared/ReactLazyStatusTags';
import {
warnAboutDeprecatedLifecycles,
disableLegacyContext,
@ -1233,42 +1231,33 @@ class ReactDOMServerRenderer {
// eslint-disable-next-line-no-fallthrough
case REACT_LAZY_TYPE: {
const element: ReactElement = (nextChild: any);
const lazyComponent: LazyComponent<any> = (nextChild: any).type;
const lazyComponent: LazyComponent<any, any> = (nextChild: any)
.type;
// Attempt to initialize lazy component regardless of whether the
// suspense server-side renderer is enabled so synchronously
// resolved constructors are supported.
initializeLazyComponentType(lazyComponent);
switch (lazyComponent._status) {
case Resolved: {
const nextChildren = [
React.createElement(
lazyComponent._result,
Object.assign({ref: element.ref}, element.props),
),
];
const frame: Frame = {
type: null,
domNamespace: parentNamespace,
children: nextChildren,
childIndex: 0,
context: context,
footer: '',
};
if (__DEV__) {
((frame: any): FrameDev).debugElementStack = [];
}
this.stack.push(frame);
return '';
}
case Rejected:
throw lazyComponent._result;
case Pending:
default:
invariant(
false,
'ReactDOMServer does not yet support lazy-loaded components.',
);
let payload = lazyComponent._payload;
let init = lazyComponent._init;
let result = init(payload);
const nextChildren = [
React.createElement(
result,
Object.assign({ref: element.ref}, element.props),
),
];
const frame: Frame = {
type: null,
domNamespace: parentNamespace,
children: nextChildren,
childIndex: 0,
context: context,
footer: '',
};
if (__DEV__) {
((frame: any): FrameDev).debugElementStack = [];
}
this.stack.push(frame);
return '';
}
// eslint-disable-next-line-no-fallthrough
case REACT_SCOPE_TYPE: {

View File

@ -10,6 +10,7 @@
import type {ReactElement} from 'shared/ReactElementType';
import type {ReactPortal} from 'shared/ReactTypes';
import type {BlockComponent} from 'react/src/ReactBlock';
import type {LazyComponent} from 'react/src/ReactLazy';
import type {Fiber} from './ReactFiber';
import type {ExpirationTime} from './ReactFiberExpirationTime';
@ -20,6 +21,7 @@ import {
REACT_ELEMENT_TYPE,
REACT_FRAGMENT_TYPE,
REACT_PORTAL_TYPE,
REACT_LAZY_TYPE,
REACT_BLOCK_TYPE,
} from 'shared/ReactSymbols';
import {
@ -48,7 +50,6 @@ import {
} from './ReactCurrentFiber';
import {isCompatibleFamilyForHotReloading} from './ReactFiberHotReloading';
import {StrictMode} from './ReactTypeOfMode';
import {initializeBlockComponentType} from 'shared/ReactLazyComponent';
let didWarnAboutMaps;
let didWarnAboutGenerators;
@ -263,6 +264,22 @@ function warnOnFunctionType() {
}
}
// We avoid inlining this to avoid potential deopts from using try/catch.
/** @noinline */
function resolveLazyType<T, P>(
lazyComponent: LazyComponent<T, P>,
): LazyComponent<T, P> | T {
try {
// If we can, let's peek at the resulting type.
let payload = lazyComponent._payload;
let init = lazyComponent._init;
return init(payload);
} catch (x) {
// Leave it in place and let it throw again in the begin phase.
return lazyComponent;
}
}
// This wrapper function exists because I expect to clone the code in each path
// to be able to optimize each path individually by branching early. This needs
// a compiler or we can do it manually. Helpers that don't need this branching
@ -419,22 +436,22 @@ function ChildReconciler(shouldTrackSideEffects) {
existing._debugOwner = element._owner;
}
return existing;
} else if (
enableBlocksAPI &&
current.tag === Block &&
element.type.$$typeof === REACT_BLOCK_TYPE
) {
} else if (enableBlocksAPI && current.tag === Block) {
// The new Block might not be initialized yet. We need to initialize
// it in case initializing it turns out it would match.
initializeBlockComponentType(element.type);
let type = element.type;
if (type.$$typeof === REACT_LAZY_TYPE) {
type = resolveLazyType(type);
}
if (
(element.type: BlockComponent<any, any, any>)._fn ===
(current.type: BlockComponent<any, any, any>)._fn
type.$$typeof === REACT_BLOCK_TYPE &&
((type: any): BlockComponent<any, any>)._render ===
(current.type: BlockComponent<any, any>)._render
) {
// Same as above but also update the .type field.
const existing = useFiber(current, element.props);
existing.return = returnFiber;
existing.type = element.type;
existing.type = type;
if (__DEV__) {
existing._debugSource = element._source;
existing._debugOwner = element._owner;
@ -1188,17 +1205,20 @@ function ChildReconciler(shouldTrackSideEffects) {
}
case Block:
if (enableBlocksAPI) {
if (element.type.$$typeof === REACT_BLOCK_TYPE) {
let type = element.type;
if (type.$$typeof === REACT_LAZY_TYPE) {
type = resolveLazyType(type);
}
if (type.$$typeof === REACT_BLOCK_TYPE) {
// The new Block might not be initialized yet. We need to initialize
// it in case initializing it turns out it would match.
initializeBlockComponentType(element.type);
if (
(element.type: BlockComponent<any, any, any>)._fn ===
(child.type: BlockComponent<any, any, any>)._fn
((type: any): BlockComponent<any, any>)._render ===
(child.type: BlockComponent<any, any>)._render
) {
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, element.props);
existing.type = element.type;
existing.type = type;
existing.return = returnFiber;
if (__DEV__) {
existing._debugSource = element._source;

View File

@ -9,6 +9,7 @@
import type {ReactProviderType, ReactContext} from 'shared/ReactTypes';
import type {BlockComponent} from 'react/src/ReactBlock';
import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy';
import type {Fiber} from './ReactFiber';
import type {FiberRoot} from './ReactFiberRoot';
import type {ExpirationTime} from './ReactFiberExpirationTime';
@ -73,7 +74,6 @@ import invariant from 'shared/invariant';
import shallowEqual from 'shared/shallowEqual';
import getComponentName from 'shared/getComponentName';
import ReactStrictModeWarnings from './ReactStrictModeWarnings';
import {refineResolvedLazyComponent} from 'shared/ReactLazyComponent';
import {REACT_LAZY_TYPE, getIteratorFn} from 'shared/ReactSymbols';
import {
getCurrentFiberOwnerNameInDevOrNull,
@ -164,11 +164,7 @@ import {
resumeMountClassInstance,
updateClassInstance,
} from './ReactFiberClassComponent';
import {
readLazyComponentType,
resolveDefaultProps,
} from './ReactFiberLazyComponent';
import {initializeBlockComponentType} from 'shared/ReactLazyComponent';
import {resolveDefaultProps} from './ReactFiberLazyComponent';
import {
resolveLazyComponentTag,
createFiberFromTypeAndProps,
@ -184,7 +180,6 @@ import {
renderDidSuspendDelayIfPossible,
markUnprocessedUpdateTime,
} from './ReactFiberWorkLoop';
import {Resolved} from 'shared/ReactLazyStatusTags';
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
@ -492,7 +487,14 @@ function updateSimpleMemoComponent(
// We warn when you define propTypes on lazy()
// so let's just skip over it to find memo() outer wrapper.
// Inner props for memo are validated later.
outerMemoType = refineResolvedLazyComponent(outerMemoType);
const lazyComponent: LazyComponentType<any, any> = outerMemoType;
let payload = lazyComponent._payload;
let init = lazyComponent._init;
try {
outerMemoType = init(payload);
} catch (x) {
outerMemoType = null;
}
}
const outerPropTypes = outerMemoType && (outerMemoType: any).propTypes;
if (outerPropTypes) {
@ -703,10 +705,10 @@ function updateFunctionComponent(
return workInProgress.child;
}
function updateBlock<Props, Payload, Data>(
function updateBlock<Props, Data>(
current: Fiber | null,
workInProgress: Fiber,
block: BlockComponent<Props, Payload, Data>,
block: BlockComponent<Props, Data>,
nextProps: any,
renderExpirationTime: ExpirationTime,
) {
@ -714,12 +716,7 @@ function updateBlock<Props, Payload, Data>(
// hasn't yet mounted. This happens after the first render suspends.
// We'll need to figure out if this is fine or can cause issues.
initializeBlockComponentType(block);
if (block._status !== Resolved) {
throw block._data;
}
const render = block._fn;
const render = block._render;
const data = block._data;
// The rest is a fork of updateFunctionComponent
@ -1142,7 +1139,10 @@ function mountLazyComponent(
// We can't start a User Timing measurement with correct label yet.
// Cancel and resume right after we know the tag.
cancelWorkTimer(workInProgress);
let Component = readLazyComponentType(elementType);
let lazyComponent: LazyComponentType<any, any> = elementType;
let payload = lazyComponent._payload;
let init = lazyComponent._init;
let Component = init(payload);
// Store the unwrapped component in the type.
workInProgress.type = Component;
const resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component));

View File

@ -7,11 +7,6 @@
* @flow
*/
import type {LazyComponent} from 'react/src/ReactLazy';
import {Resolved} from 'shared/ReactLazyStatusTags';
import {initializeLazyComponentType} from 'shared/ReactLazyComponent';
export function resolveDefaultProps(Component: any, baseProps: Object): Object {
if (Component && Component.defaultProps) {
// Resolve default props. Taken from ReactElement
@ -26,11 +21,3 @@ export function resolveDefaultProps(Component: any, baseProps: Object): Object {
}
return baseProps;
}
export function readLazyComponentType<T>(lazyComponent: LazyComponent<T>): T {
initializeLazyComponentType(lazyComponent);
if (lazyComponent._status !== Resolved) {
throw lazyComponent._result;
}
return lazyComponent._result;
}

View File

@ -14,11 +14,13 @@ let useState;
let Suspense;
let block;
let readString;
let Scheduler;
describe('ReactBlocks', () => {
beforeEach(() => {
jest.resetModules();
Scheduler = require('scheduler');
React = require('react');
ReactNoop = require('react-noop-renderer');
@ -47,6 +49,43 @@ describe('ReactBlocks', () => {
};
});
it.experimental('prints the name of the render function in warnings', () => {
function Query(firstName) {
return {
name: firstName,
};
}
function User(props, data) {
let array = [<span>{data.name}</span>];
return <div>{array}</div>;
}
function App({Component}) {
return (
<Suspense fallback={'Loading...'}>
<Component name="Name" />
</Suspense>
);
}
let loadUser = block(Query, User);
expect(() => {
ReactNoop.act(() => {
ReactNoop.render(<App Component={loadUser()} />);
});
}).toErrorDev(
'Warning: Each child in a list should have a unique ' +
'"key" prop.\n\nCheck the render method of `User`. See ' +
'https://fb.me/react-warning-keys for more information.\n' +
' in span (at **)\n' +
' in User (at **)\n' +
' in Suspense (at **)\n' +
' in App (at **)',
);
});
it.experimental('renders a component with a suspending query', async () => {
function Query(id) {
return {
@ -86,64 +125,64 @@ describe('ReactBlocks', () => {
expect(ReactNoop).toMatchRenderedOutput(<span>Name: Sebastian</span>);
});
it.experimental('supports a lazy wrapper around a chunk', async () => {
function Query(id) {
return {
id: id,
name: readString('Sebastian'),
};
}
it.experimental(
'does not support a lazy wrapper around a chunk',
async () => {
function Query(id) {
return {
id: id,
name: readString('Sebastian'),
};
}
function Render(props, data) {
return (
<span>
{props.title}: {data.name}
</span>
function Render(props, data) {
return (
<span>
{props.title}: {data.name}
</span>
);
}
let loadUser = block(Query, Render);
function App({User}) {
return (
<Suspense fallback={'Loading...'}>
<User title="Name" />
</Suspense>
);
}
let resolveLazy;
let LazyUser = React.lazy(
() =>
new Promise(resolve => {
resolveLazy = function() {
resolve({
default: loadUser(123),
});
};
}),
);
}
let loadUser = block(Query, Render);
await ReactNoop.act(async () => {
ReactNoop.render(<App User={LazyUser} />);
});
function App({User}) {
return (
<Suspense fallback={'Loading...'}>
<User title="Name" />
</Suspense>
);
}
expect(ReactNoop).toMatchRenderedOutput('Loading...');
let resolveLazy;
let LazyUser = React.lazy(
() =>
new Promise(resolve => {
resolveLazy = function() {
resolve({
default: loadUser(123),
});
};
}),
);
await ReactNoop.act(async () => {
ReactNoop.render(<App User={LazyUser} />);
});
expect(ReactNoop).toMatchRenderedOutput('Loading...');
// Resolve the component.
await ReactNoop.act(async () => {
// Resolve the component.
await resolveLazy();
});
// We're still waiting on the data.
expect(ReactNoop).toMatchRenderedOutput('Loading...');
await ReactNoop.act(async () => {
jest.advanceTimersByTime(1000);
});
expect(ReactNoop).toMatchRenderedOutput(<span>Name: Sebastian</span>);
});
expect(Scheduler).toFlushAndThrow(
'Element type is invalid. Received a promise that resolves to: [object Object]. ' +
'Lazy element type must resolve to a class or function.' +
(__DEV__
? ' Did you wrap a component in React.lazy() more than once?'
: ''),
);
},
);
it.experimental(
'can receive updated data for the same component',

View File

@ -879,7 +879,13 @@ describe('ReactLazy', () => {
},
);
expect(Scheduler).toFlushAndYield(['Started loading', 'Loading...']);
if (__DEV__) {
// Getting the name for the warning cause the loading to start early.
expect(Scheduler).toHaveYielded(['Started loading']);
expect(Scheduler).toFlushAndYield(['Loading...']);
} else {
expect(Scheduler).toFlushAndYield(['Started loading', 'Loading...']);
}
expect(root).not.toMatchRenderedOutput(<div>AB</div>);
await Promise.resolve();

View File

@ -7,7 +7,10 @@
* @flow
*/
import type {LazyComponent} from './ReactLazy';
import {
REACT_LAZY_TYPE,
REACT_BLOCK_TYPE,
REACT_MEMO_TYPE,
REACT_FORWARD_REF_TYPE,
@ -19,55 +22,33 @@ type BlockRenderFunction<Props, Data> = (
data: Data,
) => React$Node;
type Thenable<T, R> = {
then(resolve: (T) => mixed, reject: (mixed) => mixed): R,
type Payload<Props, Args: Iterable<any>, Data> = {
query: BlockQueryFunction<Args, Data>,
args: Args,
render: BlockRenderFunction<Props, Data>,
};
type Initializer<Props, Payload, Data> = (
payload: Payload,
) =>
| [Data, BlockRenderFunction<Props, Data>]
| Thenable<[Data, BlockRenderFunction<Props, Data>], mixed>;
export type UninitializedBlockComponent<Props, Payload, Data> = {
export type BlockComponent<Props, Data> = {
$$typeof: Symbol | number,
_status: -1,
_data: Payload,
_fn: Initializer<Props, Payload, Data>,
};
export type PendingBlockComponent<Props, Data> = {
$$typeof: Symbol | number,
_status: 0,
_data: Thenable<[Data, BlockRenderFunction<Props, Data>], mixed>,
_fn: null,
};
export type ResolvedBlockComponent<Props, Data> = {
$$typeof: Symbol | number,
_status: 1,
_data: Data,
_fn: BlockRenderFunction<Props, Data>,
_render: BlockRenderFunction<Props, Data>,
};
export type RejectedBlockComponent = {
$$typeof: Symbol | number,
_status: 2,
_data: mixed,
_fn: null,
};
export type BlockComponent<Props, Payload, Data> =
| UninitializedBlockComponent<Props, Payload, Data>
| PendingBlockComponent<Props, Data>
| ResolvedBlockComponent<Props, Data>
| RejectedBlockComponent;
opaque type Block<Props>: React$AbstractComponent<
Props,
null,
> = React$AbstractComponent<Props, null>;
function lazyInitializer<Props, Args: Iterable<any>, Data>(
payload: Payload<Props, Args, Data>,
): BlockComponent<Props, Data> {
return {
$$typeof: REACT_BLOCK_TYPE,
_data: payload.query.apply(null, payload.args),
_render: payload.render,
};
}
export function block<Args: Iterable<any>, Props, Data>(
query: BlockQueryFunction<Args, Data>,
render: BlockRenderFunction<Props, Data>,
@ -115,19 +96,26 @@ export function block<Args: Iterable<any>, Props, Data>(
);
}
}
function initializer(args) {
let data = query.apply(null, args);
return [data, render];
}
return function(): Block<Props> {
let args: Args = arguments;
let blockComponent: UninitializedBlockComponent<Props, Args, Data> = {
$$typeof: REACT_BLOCK_TYPE,
_status: -1,
_data: args,
_fn: initializer,
let payload: Payload<Props, Args, Data> = {
query: query,
args: args,
render: render,
};
let lazyType: LazyComponent<
BlockComponent<Props, Data>,
Payload<Props, Args, Data>,
> = {
$$typeof: REACT_LAZY_TYPE,
_payload: payload,
_init: lazyInitializer,
};
// $FlowFixMe
return blockComponent;
return lazyType;
};
}

View File

@ -13,46 +13,105 @@ type Thenable<T, R> = {
then(resolve: (T) => mixed, reject: (mixed) => mixed): R,
};
export type UninitializedLazyComponent<T> = {
$$typeof: Symbol | number,
const Uninitialized = -1;
const Pending = 0;
const Resolved = 1;
const Rejected = 2;
type UninitializedPayload<T> = {
_status: -1,
_result: () => Thenable<{default: T, ...} | T, mixed>,
_result: () => Thenable<{default: T, ...}, mixed>,
};
export type PendingLazyComponent<T> = {
$$typeof: Symbol | number,
type PendingPayload<T> = {
_status: 0,
_result: Thenable<{default: T, ...} | T, mixed>,
_result: Thenable<{default: T, ...}, mixed>,
};
export type ResolvedLazyComponent<T> = {
$$typeof: Symbol | number,
type ResolvedPayload<T> = {
_status: 1,
_result: T,
};
export type RejectedLazyComponent = {
$$typeof: Symbol | number,
type RejectedPayload = {
_status: 2,
_result: mixed,
};
export type LazyComponent<T> =
| UninitializedLazyComponent<T>
| PendingLazyComponent<T>
| ResolvedLazyComponent<T>
| RejectedLazyComponent;
type Payload<T> =
| UninitializedPayload<T>
| PendingPayload<T>
| ResolvedPayload<T>
| RejectedPayload;
export type LazyComponent<T, P> = {
$$typeof: Symbol | number,
_payload: P,
_init: (payload: P) => T,
};
function lazyInitializer<T>(payload: Payload<T>): T {
if (payload._status === Uninitialized) {
const ctor = payload._result;
const thenable = ctor();
// Transition to the next state.
const pending: PendingPayload<any> = (payload: any);
pending._status = Pending;
pending._result = thenable;
thenable.then(
moduleObject => {
if (payload._status === Pending) {
const defaultExport = moduleObject.default;
if (__DEV__) {
if (defaultExport === undefined) {
console.error(
'lazy: Expected the result of a dynamic import() call. ' +
'Instead received: %s\n\nYour code should look like: \n ' +
// Break up imports to avoid accidentally parsing them as dependencies.
'const MyComponent = lazy(() => imp' +
"ort('./MyComponent'))",
moduleObject,
);
}
}
// Transition to the next state.
const resolved: ResolvedPayload<any> = (payload: any);
resolved._status = Resolved;
resolved._result = defaultExport;
}
},
error => {
if (payload._status === Pending) {
// Transition to the next state.
const rejected: RejectedPayload = (payload: any);
rejected._status = Rejected;
rejected._result = error;
}
},
);
}
if (payload._status === Resolved) {
return payload._result;
} else {
throw payload._result;
}
}
export function lazy<T>(
ctor: () => Thenable<{default: T, ...} | T, mixed>,
): LazyComponent<T> {
let lazyType: LazyComponent<T> = {
$$typeof: REACT_LAZY_TYPE,
// React uses these fields to store the result.
ctor: () => Thenable<{default: T, ...}, mixed>,
): LazyComponent<T, Payload<T>> {
let payload: Payload<T> = {
// We use these fields to store the result.
_status: -1,
_result: ctor,
};
let lazyType: LazyComponent<T, Payload<T>> = {
$$typeof: REACT_LAZY_TYPE,
_payload: payload,
_init: lazyInitializer,
};
if (__DEV__) {
// In production, this would just set it on the object.
let defaultProps;

View File

@ -1,126 +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
*/
import type {
PendingLazyComponent,
ResolvedLazyComponent,
RejectedLazyComponent,
LazyComponent,
} from 'react/src/ReactLazy';
import type {
PendingBlockComponent,
ResolvedBlockComponent,
RejectedBlockComponent,
BlockComponent,
} from 'react/src/ReactBlock';
import {
Uninitialized,
Pending,
Resolved,
Rejected,
} from './ReactLazyStatusTags';
export function refineResolvedLazyComponent<T>(
lazyComponent: LazyComponent<T>,
): T | null {
return lazyComponent._status === Resolved ? lazyComponent._result : null;
}
export function initializeLazyComponentType(
lazyComponent: LazyComponent<any>,
): void {
if (lazyComponent._status === Uninitialized) {
const ctor = lazyComponent._result;
const thenable = ctor();
// Transition to the next state.
const pending: PendingLazyComponent<any> = (lazyComponent: any);
pending._status = Pending;
pending._result = thenable;
thenable.then(
moduleObject => {
if (lazyComponent._status === Pending) {
const defaultExport = moduleObject.default;
if (__DEV__) {
if (defaultExport === undefined) {
console.error(
'lazy: Expected the result of a dynamic import() call. ' +
'Instead received: %s\n\nYour code should look like: \n ' +
// Break up imports to avoid accidentally parsing them as dependencies.
'const MyComponent = lazy(() => imp' +
"ort('./MyComponent'))",
moduleObject,
);
}
}
// Transition to the next state.
const resolved: ResolvedLazyComponent<any> = (lazyComponent: any);
resolved._status = Resolved;
resolved._result = defaultExport;
}
},
error => {
if (lazyComponent._status === Pending) {
// Transition to the next state.
const rejected: RejectedLazyComponent = (lazyComponent: any);
rejected._status = Rejected;
rejected._result = error;
}
},
);
}
}
export function initializeBlockComponentType<Props, Payload, Data>(
blockComponent: BlockComponent<Props, Payload, Data>,
): void {
if (blockComponent._status === Uninitialized) {
const thenableOrTuple = blockComponent._fn(blockComponent._data);
if (typeof thenableOrTuple.then !== 'function') {
let tuple: [any, any] = (thenableOrTuple: any);
const resolved: ResolvedBlockComponent<
Props,
Data,
> = (blockComponent: any);
resolved._status = Resolved;
resolved._data = tuple[0];
resolved._fn = tuple[1];
return;
}
const thenable = (thenableOrTuple: any);
// Transition to the next state.
const pending: PendingBlockComponent<Props, Data> = (blockComponent: any);
pending._status = Pending;
pending._data = thenable;
pending._fn = null;
thenable.then(
(tuple: [any, any]) => {
if (blockComponent._status === Pending) {
// Transition to the next state.
const resolved: ResolvedBlockComponent<
Props,
Data,
> = (blockComponent: any);
resolved._status = Resolved;
resolved._data = tuple[0];
resolved._fn = tuple[1];
}
},
error => {
if (blockComponent._status === Pending) {
// Transition to the next state.
const rejected: RejectedBlockComponent = (blockComponent: any);
rejected._status = Rejected;
rejected._data = error;
}
},
);
}
}

View File

@ -1,14 +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
*/
// TODO: Move this to "react" once we can import from externals.
export const Uninitialized = -1;
export const Pending = 0;
export const Resolved = 1;
export const Rejected = 2;

View File

@ -23,7 +23,6 @@ import {
REACT_LAZY_TYPE,
REACT_BLOCK_TYPE,
} from 'shared/ReactSymbols';
import {refineResolvedLazyComponent} from 'shared/ReactLazyComponent';
import type {ReactContext, ReactProviderType} from 'shared/ReactTypes';
function getWrappedName(
@ -88,14 +87,16 @@ function getComponentName(type: mixed): string | null {
case REACT_MEMO_TYPE:
return getComponentName(type.type);
case REACT_BLOCK_TYPE:
return getComponentName(type.render);
return getComponentName(type._render);
case REACT_LAZY_TYPE: {
const thenable: LazyComponent<mixed> = (type: any);
const resolvedThenable = refineResolvedLazyComponent(thenable);
if (resolvedThenable) {
return getComponentName(resolvedThenable);
const lazyComponent: LazyComponent<any, any> = (type: any);
let payload = lazyComponent._payload;
let init = lazyComponent._init;
try {
return getComponentName(init(payload));
} catch (x) {
return null;
}
break;
}
}
}