From c2404c8b53022516f6c88eca7dad85610e4e1768 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Wed, 4 Mar 2020 17:42:27 -0800 Subject: [PATCH 1/6] This type is all wrong and nothing cares because it's all any --- packages/shared/ReactLazyComponent.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/shared/ReactLazyComponent.js b/packages/shared/ReactLazyComponent.js index 246ba41e83da5..b6b1fce8430cb 100644 --- a/packages/shared/ReactLazyComponent.js +++ b/packages/shared/ReactLazyComponent.js @@ -20,14 +20,6 @@ export type LazyComponent = { ... }; -type ResolvedLazyComponent = { - $$typeof: Symbol | number, - _ctor: () => Thenable<{default: T, ...}, mixed>, - _status: 1, - _result: any, - ... -}; - export const Uninitialized = -1; export const Pending = 0; export const Resolved = 1; @@ -35,7 +27,7 @@ export const Rejected = 2; export function refineResolvedLazyComponent( lazyComponent: LazyComponent, -): ResolvedLazyComponent | null { +): T | null { return lazyComponent._status === Resolved ? lazyComponent._result : null; } From a1627d731e41734720456fd28b7758b943e7d6ee Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Wed, 4 Mar 2020 19:44:19 -0800 Subject: [PATCH 2/6] Refine Flow types of Lazy Components We can type each condition. --- packages/react/src/ReactLazy.js | 11 +++++- packages/shared/ReactLazyComponent.js | 55 +++++++++++++++++++++------ 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/packages/react/src/ReactLazy.js b/packages/react/src/ReactLazy.js index c804c53741a60..4f8d9cd959c34 100644 --- a/packages/react/src/ReactLazy.js +++ b/packages/react/src/ReactLazy.js @@ -3,14 +3,18 @@ * * 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 {LazyComponent, Thenable} from 'shared/ReactLazyComponent'; import {REACT_LAZY_TYPE} from 'shared/ReactSymbols'; -export function lazy(ctor: () => Thenable): LazyComponent { - let lazyType = { +export function lazy( + ctor: () => Thenable<{default: T, ...} | T, mixed>, +): LazyComponent { + let lazyType: LazyComponent = { $$typeof: REACT_LAZY_TYPE, _ctor: ctor, // React uses these fields to store the result. @@ -22,6 +26,7 @@ export function lazy(ctor: () => Thenable): LazyComponent { // In production, this would just set it on the object. let defaultProps; let propTypes; + // $FlowFixMe Object.defineProperties(lazyType, { defaultProps: { configurable: true, @@ -36,6 +41,7 @@ export function lazy(ctor: () => Thenable): LazyComponent { ); defaultProps = newDefaultProps; // Match production behavior more closely: + // $FlowFixMe Object.defineProperty(lazyType, 'defaultProps', { enumerable: true, }); @@ -54,6 +60,7 @@ export function lazy(ctor: () => Thenable): LazyComponent { ); propTypes = newPropTypes; // Match production behavior more closely: + // $FlowFixMe Object.defineProperty(lazyType, 'propTypes', { enumerable: true, }); diff --git a/packages/shared/ReactLazyComponent.js b/packages/shared/ReactLazyComponent.js index b6b1fce8430cb..7a180ba8f708f 100644 --- a/packages/shared/ReactLazyComponent.js +++ b/packages/shared/ReactLazyComponent.js @@ -9,17 +9,42 @@ export type Thenable = { then(resolve: (T) => mixed, reject: (mixed) => mixed): R, - ... }; -export type LazyComponent = { +type UninitializedLazyComponent = { $$typeof: Symbol | number, - _ctor: () => Thenable<{default: T, ...}, mixed>, - _status: 0 | 1 | 2, - _result: any, - ... + _ctor: () => Thenable<{default: T, ...} | T, mixed>, + _status: -1, + _result: null, }; +type PendingLazyComponent = { + $$typeof: Symbol | number, + _ctor: () => Thenable<{default: T, ...} | T, mixed>, + _status: 0, + _result: Thenable<{default: T, ...} | T, mixed>, +}; + +type ResolvedLazyComponent = { + $$typeof: Symbol | number, + _ctor: () => Thenable<{default: T, ...} | T, mixed>, + _status: 1, + _result: T, +}; + +type RejectedLazyComponent = { + $$typeof: Symbol | number, + _ctor: () => Thenable<{default: T, ...} | T, mixed>, + _status: 2, + _result: mixed, +}; + +export type LazyComponent = + | UninitializedLazyComponent + | PendingLazyComponent + | ResolvedLazyComponent + | RejectedLazyComponent; + export const Uninitialized = -1; export const Pending = 0; export const Resolved = 1; @@ -35,10 +60,12 @@ export function initializeLazyComponentType( lazyComponent: LazyComponent, ): void { if (lazyComponent._status === Uninitialized) { - lazyComponent._status = Pending; const ctor = lazyComponent._ctor; const thenable = ctor(); - lazyComponent._result = thenable; + // Transition to the next state. + const pending: PendingLazyComponent = (lazyComponent: any); + pending._status = Pending; + pending._result = thenable; thenable.then( moduleObject => { if (lazyComponent._status === Pending) { @@ -53,14 +80,18 @@ export function initializeLazyComponentType( ); } } - lazyComponent._status = Resolved; - lazyComponent._result = defaultExport; + // Transition to the next state. + const resolved: ResolvedLazyComponent = (lazyComponent: any); + resolved._status = Resolved; + resolved._result = defaultExport; } }, error => { if (lazyComponent._status === Pending) { - lazyComponent._status = Rejected; - lazyComponent._result = error; + // Transition to the next state. + const rejected: RejectedLazyComponent = (lazyComponent: any); + rejected._status = Rejected; + rejected._result = error; } }, ); From 1ac618d1acda99bacab33f854c14e66f48150e04 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Wed, 4 Mar 2020 19:55:53 -0800 Subject: [PATCH 3/6] 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. --- packages/react/src/ReactLazy.js | 3 +-- packages/shared/ReactLazyComponent.js | 8 ++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/react/src/ReactLazy.js b/packages/react/src/ReactLazy.js index 4f8d9cd959c34..0b616442bdb19 100644 --- a/packages/react/src/ReactLazy.js +++ b/packages/react/src/ReactLazy.js @@ -16,10 +16,9 @@ export function lazy( ): LazyComponent { let lazyType: LazyComponent = { $$typeof: REACT_LAZY_TYPE, - _ctor: ctor, // React uses these fields to store the result. _status: -1, - _result: null, + _result: ctor, }; if (__DEV__) { diff --git a/packages/shared/ReactLazyComponent.js b/packages/shared/ReactLazyComponent.js index 7a180ba8f708f..f9030eaf91128 100644 --- a/packages/shared/ReactLazyComponent.js +++ b/packages/shared/ReactLazyComponent.js @@ -13,28 +13,24 @@ export type Thenable = { type UninitializedLazyComponent = { $$typeof: Symbol | number, - _ctor: () => Thenable<{default: T, ...} | T, mixed>, _status: -1, - _result: null, + _result: () => Thenable<{default: T, ...} | T, mixed>, }; type PendingLazyComponent = { $$typeof: Symbol | number, - _ctor: () => Thenable<{default: T, ...} | T, mixed>, _status: 0, _result: Thenable<{default: T, ...} | T, mixed>, }; type ResolvedLazyComponent = { $$typeof: Symbol | number, - _ctor: () => Thenable<{default: T, ...} | T, mixed>, _status: 1, _result: T, }; type RejectedLazyComponent = { $$typeof: Symbol | number, - _ctor: () => Thenable<{default: T, ...} | T, mixed>, _status: 2, _result: mixed, }; @@ -60,7 +56,7 @@ export function initializeLazyComponentType( lazyComponent: LazyComponent, ): void { if (lazyComponent._status === Uninitialized) { - const ctor = lazyComponent._ctor; + const ctor = lazyComponent._result; const thenable = ctor(); // Transition to the next state. const pending: PendingLazyComponent = (lazyComponent: any); From 1b37fc2cf47ca09c1e902a25ea607d3ed733caf6 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Wed, 4 Mar 2020 19:56:52 -0800 Subject: [PATCH 4/6] 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. --- packages/shared/ReactLazyComponent.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/shared/ReactLazyComponent.js b/packages/shared/ReactLazyComponent.js index f9030eaf91128..c44b962888a3d 100644 --- a/packages/shared/ReactLazyComponent.js +++ b/packages/shared/ReactLazyComponent.js @@ -56,7 +56,14 @@ export function initializeLazyComponentType( lazyComponent: LazyComponent, ): void { if (lazyComponent._status === Uninitialized) { - const ctor = lazyComponent._result; + let ctor = lazyComponent._result; + if (!ctor) { + // TODO: Remove this later. THis only exists in case you use an older "react" package. + ctor = ((lazyComponent: any)._ctor: () => Thenable< + {default: any, ...} | any, + mixed, + >); + } const thenable = ctor(); // Transition to the next state. const pending: PendingLazyComponent = (lazyComponent: any); From 326c7107a3f3c7cc7ba9d16d32dbec65a6696eaf Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Wed, 4 Mar 2020 20:25:43 -0800 Subject: [PATCH 5/6] 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. --- .../src/server/ReactPartialRenderer.js | 10 ++-- .../src/ReactFiberLazyComponent.js | 5 +- packages/react/src/ReactLazy.js | 43 ++++++++++++++-- packages/shared/ReactLazyComponent.js | 49 +++---------------- packages/shared/getComponentName.js | 2 +- 5 files changed, 55 insertions(+), 54 deletions(-) diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index e06f1df9bcd88..bf77e7b325fed 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -9,7 +9,7 @@ import type {ThreadID} from './ReactThreadIDAllocator'; 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 * as React from 'react'; @@ -17,12 +17,8 @@ import invariant from 'shared/invariant'; import getComponentName from 'shared/getComponentName'; import describeComponentFrame from 'shared/describeComponentFrame'; import ReactSharedInternals from 'shared/ReactSharedInternals'; -import { - Resolved, - Rejected, - Pending, - initializeLazyComponentType, -} from 'shared/ReactLazyComponent'; +import {initializeLazyComponentType} from 'shared/ReactLazyComponent'; +import {Resolved, Rejected, Pending} from 'react/src/ReactLazy'; import { warnAboutDeprecatedLifecycles, disableLegacyContext, diff --git a/packages/react-reconciler/src/ReactFiberLazyComponent.js b/packages/react-reconciler/src/ReactFiberLazyComponent.js index 290458eb96928..9507ae67dfe53 100644 --- a/packages/react-reconciler/src/ReactFiberLazyComponent.js +++ b/packages/react-reconciler/src/ReactFiberLazyComponent.js @@ -7,9 +7,10 @@ * @flow */ -import type {LazyComponent} from 'shared/ReactLazyComponent'; +import type {LazyComponent} from 'react/src/ReactLazy'; -import {Resolved, initializeLazyComponentType} from 'shared/ReactLazyComponent'; +import {Resolved} from 'react/src/ReactLazy'; +import {initializeLazyComponentType} from 'shared/ReactLazyComponent'; export function resolveDefaultProps(Component: any, baseProps: Object): Object { if (Component && Component.defaultProps) { diff --git a/packages/react/src/ReactLazy.js b/packages/react/src/ReactLazy.js index 0b616442bdb19..f4633660f723e 100644 --- a/packages/react/src/ReactLazy.js +++ b/packages/react/src/ReactLazy.js @@ -7,17 +7,54 @@ * @flow */ -import type {LazyComponent, Thenable} from 'shared/ReactLazyComponent'; - import {REACT_LAZY_TYPE} from 'shared/ReactSymbols'; +type Thenable = { + then(resolve: (T) => mixed, reject: (mixed) => mixed): R, +}; + +export type UninitializedLazyComponent = { + $$typeof: Symbol | number, + _status: -1, + _result: () => Thenable<{default: T, ...} | T, mixed>, +}; + +export type PendingLazyComponent = { + $$typeof: Symbol | number, + _status: 0, + _result: Thenable<{default: T, ...} | T, mixed>, +}; + +export type ResolvedLazyComponent = { + $$typeof: Symbol | number, + _status: 1, + _result: T, +}; + +export type RejectedLazyComponent = { + $$typeof: Symbol | number, + _status: 2, + _result: mixed, +}; + +export type LazyComponent = + | UninitializedLazyComponent + | PendingLazyComponent + | ResolvedLazyComponent + | RejectedLazyComponent; + +export const Uninitialized = -1; +export const Pending = 0; +export const Resolved = 1; +export const Rejected = 2; + export function lazy( ctor: () => Thenable<{default: T, ...} | T, mixed>, ): LazyComponent { let lazyType: LazyComponent = { $$typeof: REACT_LAZY_TYPE, // React uses these fields to store the result. - _status: -1, + _status: Uninitialized, _result: ctor, }; diff --git a/packages/shared/ReactLazyComponent.js b/packages/shared/ReactLazyComponent.js index c44b962888a3d..5ecc5aae210ab 100644 --- a/packages/shared/ReactLazyComponent.js +++ b/packages/shared/ReactLazyComponent.js @@ -7,44 +7,14 @@ * @flow */ -export type Thenable = { - then(resolve: (T) => mixed, reject: (mixed) => mixed): R, -}; +import type { + PendingLazyComponent, + ResolvedLazyComponent, + RejectedLazyComponent, + LazyComponent, +} from 'react/src/ReactLazy'; -type UninitializedLazyComponent = { - $$typeof: Symbol | number, - _status: -1, - _result: () => Thenable<{default: T, ...} | T, mixed>, -}; - -type PendingLazyComponent = { - $$typeof: Symbol | number, - _status: 0, - _result: Thenable<{default: T, ...} | T, mixed>, -}; - -type ResolvedLazyComponent = { - $$typeof: Symbol | number, - _status: 1, - _result: T, -}; - -type RejectedLazyComponent = { - $$typeof: Symbol | number, - _status: 2, - _result: mixed, -}; - -export type LazyComponent = - | UninitializedLazyComponent - | PendingLazyComponent - | ResolvedLazyComponent - | RejectedLazyComponent; - -export const Uninitialized = -1; -export const Pending = 0; -export const Resolved = 1; -export const Rejected = 2; +import {Uninitialized, Pending, Resolved, Rejected} from 'react/src/ReactLazy'; export function refineResolvedLazyComponent( lazyComponent: LazyComponent, @@ -59,10 +29,7 @@ export function initializeLazyComponentType( let ctor = lazyComponent._result; if (!ctor) { // TODO: Remove this later. THis only exists in case you use an older "react" package. - ctor = ((lazyComponent: any)._ctor: () => Thenable< - {default: any, ...} | any, - mixed, - >); + ctor = ((lazyComponent: any)._ctor: typeof ctor); } const thenable = ctor(); // Transition to the next state. diff --git a/packages/shared/getComponentName.js b/packages/shared/getComponentName.js index 9694732ce5d96..9817ea21963ee 100644 --- a/packages/shared/getComponentName.js +++ b/packages/shared/getComponentName.js @@ -7,7 +7,7 @@ * @flow */ -import type {LazyComponent} from 'shared/ReactLazyComponent'; +import type {LazyComponent} from 'react/src/ReactLazy'; import { REACT_CONTEXT_TYPE, From 01bf1970c67bb0edf5513ad62abd485993cdfa01 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Wed, 4 Mar 2020 20:43:34 -0800 Subject: [PATCH 6/6] 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. --- .../react-dom/src/server/ReactPartialRenderer.js | 2 +- .../src/ReactFiberLazyComponent.js | 2 +- packages/react/src/ReactLazy.js | 7 +------ packages/shared/ReactLazyComponent.js | 7 ++++++- packages/shared/ReactLazyStatusTags.js | 14 ++++++++++++++ 5 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 packages/shared/ReactLazyStatusTags.js diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index bf77e7b325fed..a28b41fc12c90 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -18,7 +18,7 @@ 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 'react/src/ReactLazy'; +import {Resolved, Rejected, Pending} from 'shared/ReactLazyStatusTags'; import { warnAboutDeprecatedLifecycles, disableLegacyContext, diff --git a/packages/react-reconciler/src/ReactFiberLazyComponent.js b/packages/react-reconciler/src/ReactFiberLazyComponent.js index 9507ae67dfe53..394b35ae906d8 100644 --- a/packages/react-reconciler/src/ReactFiberLazyComponent.js +++ b/packages/react-reconciler/src/ReactFiberLazyComponent.js @@ -9,7 +9,7 @@ import type {LazyComponent} from 'react/src/ReactLazy'; -import {Resolved} from 'react/src/ReactLazy'; +import {Resolved} from 'shared/ReactLazyStatusTags'; import {initializeLazyComponentType} from 'shared/ReactLazyComponent'; export function resolveDefaultProps(Component: any, baseProps: Object): Object { diff --git a/packages/react/src/ReactLazy.js b/packages/react/src/ReactLazy.js index f4633660f723e..1de3189c923c6 100644 --- a/packages/react/src/ReactLazy.js +++ b/packages/react/src/ReactLazy.js @@ -43,18 +43,13 @@ export type LazyComponent = | ResolvedLazyComponent | RejectedLazyComponent; -export const Uninitialized = -1; -export const Pending = 0; -export const Resolved = 1; -export const Rejected = 2; - export function lazy( ctor: () => Thenable<{default: T, ...} | T, mixed>, ): LazyComponent { let lazyType: LazyComponent = { $$typeof: REACT_LAZY_TYPE, // React uses these fields to store the result. - _status: Uninitialized, + _status: -1, _result: ctor, }; diff --git a/packages/shared/ReactLazyComponent.js b/packages/shared/ReactLazyComponent.js index 5ecc5aae210ab..24b360a1e57a1 100644 --- a/packages/shared/ReactLazyComponent.js +++ b/packages/shared/ReactLazyComponent.js @@ -14,7 +14,12 @@ import type { LazyComponent, } from 'react/src/ReactLazy'; -import {Uninitialized, Pending, Resolved, Rejected} from 'react/src/ReactLazy'; +import { + Uninitialized, + Pending, + Resolved, + Rejected, +} from './ReactLazyStatusTags'; export function refineResolvedLazyComponent( lazyComponent: LazyComponent, diff --git a/packages/shared/ReactLazyStatusTags.js b/packages/shared/ReactLazyStatusTags.js new file mode 100644 index 0000000000000..8baa0ad217248 --- /dev/null +++ b/packages/shared/ReactLazyStatusTags.js @@ -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;