Remove _ctor field from Lazy components (#18217)

* This type is all wrong and nothing cares because it's all any

* Refine Flow types of Lazy Components

We can type each condition.

* Remove _ctor field from Lazy components

This field is not needed because it's only used before we've initialized,
and we don't have anything else to store before we've initialized.

* Check for _ctor in case it's an older isomorphic that created it

We try not to break across minors but it's no guarantee.

* Move types and constants from shared to isomorphic

The "react" package owns the data structure of the Lazy component. It
creates it and decides how any downstream renderer may use it.

* Move constants to shared

Apparently we can't depend on react/src/ because the whole package is
considered "external" as far as rollup is concerned.
This commit is contained in:
Sebastian Markbåge 2020-03-04 20:52:48 -08:00 committed by GitHub
parent 2fe0fbb05e
commit fa03206ee4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 95 additions and 48 deletions

View File

@ -9,7 +9,7 @@
import type {ThreadID} from './ReactThreadIDAllocator'; import type {ThreadID} from './ReactThreadIDAllocator';
import type {ReactElement} from 'shared/ReactElementType'; import type {ReactElement} from 'shared/ReactElementType';
import type {LazyComponent} from 'shared/ReactLazyComponent'; import type {LazyComponent} from 'react/src/ReactLazy';
import type {ReactProvider, ReactContext} from 'shared/ReactTypes'; import type {ReactProvider, ReactContext} from 'shared/ReactTypes';
import * as React from 'react'; import * as React from 'react';
@ -17,12 +17,8 @@ import invariant from 'shared/invariant';
import getComponentName from 'shared/getComponentName'; import getComponentName from 'shared/getComponentName';
import describeComponentFrame from 'shared/describeComponentFrame'; import describeComponentFrame from 'shared/describeComponentFrame';
import ReactSharedInternals from 'shared/ReactSharedInternals'; import ReactSharedInternals from 'shared/ReactSharedInternals';
import { import {initializeLazyComponentType} from 'shared/ReactLazyComponent';
Resolved, import {Resolved, Rejected, Pending} from 'shared/ReactLazyStatusTags';
Rejected,
Pending,
initializeLazyComponentType,
} from 'shared/ReactLazyComponent';
import { import {
warnAboutDeprecatedLifecycles, warnAboutDeprecatedLifecycles,
disableLegacyContext, disableLegacyContext,

View File

@ -7,9 +7,10 @@
* @flow * @flow
*/ */
import type {LazyComponent} from 'shared/ReactLazyComponent'; import type {LazyComponent} from 'react/src/ReactLazy';
import {Resolved, initializeLazyComponentType} from 'shared/ReactLazyComponent'; import {Resolved} from 'shared/ReactLazyStatusTags';
import {initializeLazyComponentType} from 'shared/ReactLazyComponent';
export function resolveDefaultProps(Component: any, baseProps: Object): Object { export function resolveDefaultProps(Component: any, baseProps: Object): Object {
if (Component && Component.defaultProps) { if (Component && Component.defaultProps) {

View File

@ -3,25 +3,61 @@
* *
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*
* @flow
*/ */
import type {LazyComponent, Thenable} from 'shared/ReactLazyComponent';
import {REACT_LAZY_TYPE} from 'shared/ReactSymbols'; import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';
export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> { type Thenable<T, R> = {
let lazyType = { then(resolve: (T) => mixed, reject: (mixed) => mixed): R,
};
export type UninitializedLazyComponent<T> = {
$$typeof: Symbol | number,
_status: -1,
_result: () => Thenable<{default: T, ...} | T, mixed>,
};
export type PendingLazyComponent<T> = {
$$typeof: Symbol | number,
_status: 0,
_result: Thenable<{default: T, ...} | T, mixed>,
};
export type ResolvedLazyComponent<T> = {
$$typeof: Symbol | number,
_status: 1,
_result: T,
};
export type RejectedLazyComponent = {
$$typeof: Symbol | number,
_status: 2,
_result: mixed,
};
export type LazyComponent<T> =
| UninitializedLazyComponent<T>
| PendingLazyComponent<T>
| ResolvedLazyComponent<T>
| RejectedLazyComponent;
export function lazy<T>(
ctor: () => Thenable<{default: T, ...} | T, mixed>,
): LazyComponent<T> {
let lazyType: LazyComponent<T> = {
$$typeof: REACT_LAZY_TYPE, $$typeof: REACT_LAZY_TYPE,
_ctor: ctor,
// React uses these fields to store the result. // React uses these fields to store the result.
_status: -1, _status: -1,
_result: null, _result: ctor,
}; };
if (__DEV__) { if (__DEV__) {
// In production, this would just set it on the object. // In production, this would just set it on the object.
let defaultProps; let defaultProps;
let propTypes; let propTypes;
// $FlowFixMe
Object.defineProperties(lazyType, { Object.defineProperties(lazyType, {
defaultProps: { defaultProps: {
configurable: true, configurable: true,
@ -36,6 +72,7 @@ export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> {
); );
defaultProps = newDefaultProps; defaultProps = newDefaultProps;
// Match production behavior more closely: // Match production behavior more closely:
// $FlowFixMe
Object.defineProperty(lazyType, 'defaultProps', { Object.defineProperty(lazyType, 'defaultProps', {
enumerable: true, enumerable: true,
}); });
@ -54,6 +91,7 @@ export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> {
); );
propTypes = newPropTypes; propTypes = newPropTypes;
// Match production behavior more closely: // Match production behavior more closely:
// $FlowFixMe
Object.defineProperty(lazyType, 'propTypes', { Object.defineProperty(lazyType, 'propTypes', {
enumerable: true, enumerable: true,
}); });

View File

@ -7,35 +7,23 @@
* @flow * @flow
*/ */
export type Thenable<T, R> = { import type {
then(resolve: (T) => mixed, reject: (mixed) => mixed): R, PendingLazyComponent,
... ResolvedLazyComponent,
}; RejectedLazyComponent,
LazyComponent,
} from 'react/src/ReactLazy';
export type LazyComponent<T> = { import {
$$typeof: Symbol | number, Uninitialized,
_ctor: () => Thenable<{default: T, ...}, mixed>, Pending,
_status: 0 | 1 | 2, Resolved,
_result: any, Rejected,
... } from './ReactLazyStatusTags';
};
type ResolvedLazyComponent<T> = {
$$typeof: Symbol | number,
_ctor: () => Thenable<{default: T, ...}, mixed>,
_status: 1,
_result: any,
...
};
export const Uninitialized = -1;
export const Pending = 0;
export const Resolved = 1;
export const Rejected = 2;
export function refineResolvedLazyComponent<T>( export function refineResolvedLazyComponent<T>(
lazyComponent: LazyComponent<T>, lazyComponent: LazyComponent<T>,
): ResolvedLazyComponent<T> | null { ): T | null {
return lazyComponent._status === Resolved ? lazyComponent._result : null; return lazyComponent._status === Resolved ? lazyComponent._result : null;
} }
@ -43,10 +31,16 @@ export function initializeLazyComponentType(
lazyComponent: LazyComponent<any>, lazyComponent: LazyComponent<any>,
): void { ): void {
if (lazyComponent._status === Uninitialized) { if (lazyComponent._status === Uninitialized) {
lazyComponent._status = Pending; let ctor = lazyComponent._result;
const ctor = lazyComponent._ctor; if (!ctor) {
// TODO: Remove this later. THis only exists in case you use an older "react" package.
ctor = ((lazyComponent: any)._ctor: typeof ctor);
}
const thenable = ctor(); const thenable = ctor();
lazyComponent._result = thenable; // Transition to the next state.
const pending: PendingLazyComponent<any> = (lazyComponent: any);
pending._status = Pending;
pending._result = thenable;
thenable.then( thenable.then(
moduleObject => { moduleObject => {
if (lazyComponent._status === Pending) { if (lazyComponent._status === Pending) {
@ -61,14 +55,18 @@ export function initializeLazyComponentType(
); );
} }
} }
lazyComponent._status = Resolved; // Transition to the next state.
lazyComponent._result = defaultExport; const resolved: ResolvedLazyComponent<any> = (lazyComponent: any);
resolved._status = Resolved;
resolved._result = defaultExport;
} }
}, },
error => { error => {
if (lazyComponent._status === Pending) { if (lazyComponent._status === Pending) {
lazyComponent._status = Rejected; // Transition to the next state.
lazyComponent._result = error; const rejected: RejectedLazyComponent = (lazyComponent: any);
rejected._status = Rejected;
rejected._result = error;
} }
}, },
); );

View File

@ -0,0 +1,14 @@
/**
* 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

@ -7,7 +7,7 @@
* @flow * @flow
*/ */
import type {LazyComponent} from 'shared/ReactLazyComponent'; import type {LazyComponent} from 'react/src/ReactLazy';
import { import {
REACT_CONTEXT_TYPE, REACT_CONTEXT_TYPE,