Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
/**
|
2018-09-08 06:11:23 +08:00
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
*
|
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
|
*
|
|
|
|
|
* @flow
|
|
|
|
|
*/
|
|
|
|
|
|
2019-08-14 09:51:20 +08:00
|
|
|
import warning from 'shared/warning';
|
|
|
|
|
|
2018-10-19 10:57:12 +08:00
|
|
|
export type Thenable<T, R> = {
|
|
|
|
|
then(resolve: (T) => mixed, reject: (mixed) => mixed): R,
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
};
|
|
|
|
|
|
2018-10-19 10:57:12 +08:00
|
|
|
export type LazyComponent<T> = {
|
|
|
|
|
$$typeof: Symbol | number,
|
2018-10-19 10:58:25 +08:00
|
|
|
_ctor: () => Thenable<{default: T}, mixed>,
|
2018-10-19 10:57:12 +08:00
|
|
|
_status: 0 | 1 | 2,
|
|
|
|
|
_result: any,
|
|
|
|
|
};
|
|
|
|
|
|
2018-10-19 10:58:25 +08:00
|
|
|
type ResolvedLazyComponent<T> = {
|
2018-10-19 10:57:12 +08:00
|
|
|
$$typeof: Symbol | number,
|
2018-10-19 10:58:25 +08:00
|
|
|
_ctor: () => Thenable<{default: T}, mixed>,
|
2018-10-19 10:57:12 +08:00
|
|
|
_status: 1,
|
|
|
|
|
_result: any,
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
};
|
|
|
|
|
|
2019-08-14 09:51:20 +08:00
|
|
|
export const Uninitialized = -1;
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
export const Pending = 0;
|
|
|
|
|
export const Resolved = 1;
|
|
|
|
|
export const Rejected = 2;
|
|
|
|
|
|
2018-10-19 10:57:12 +08:00
|
|
|
export function refineResolvedLazyComponent<T>(
|
|
|
|
|
lazyComponent: LazyComponent<T>,
|
2018-10-19 10:58:25 +08:00
|
|
|
): ResolvedLazyComponent<T> | null {
|
2018-10-19 10:57:12 +08:00
|
|
|
return lazyComponent._status === Resolved ? lazyComponent._result : null;
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
}
|
2019-08-14 09:51:20 +08:00
|
|
|
|
|
|
|
|
export function initializeLazyComponentType(
|
|
|
|
|
lazyComponent: LazyComponent<any>,
|
|
|
|
|
): void {
|
|
|
|
|
if (lazyComponent._status === Uninitialized) {
|
|
|
|
|
lazyComponent._status = Pending;
|
|
|
|
|
const ctor = lazyComponent._ctor;
|
|
|
|
|
const thenable = ctor();
|
|
|
|
|
lazyComponent._result = thenable;
|
|
|
|
|
thenable.then(
|
|
|
|
|
moduleObject => {
|
|
|
|
|
if (lazyComponent._status === Pending) {
|
|
|
|
|
const defaultExport = moduleObject.default;
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
if (defaultExport === undefined) {
|
|
|
|
|
warning(
|
|
|
|
|
false,
|
|
|
|
|
'lazy: Expected the result of a dynamic import() call. ' +
|
|
|
|
|
'Instead received: %s\n\nYour code should look like: \n ' +
|
|
|
|
|
"const MyComponent = lazy(() => import('./MyComponent'))",
|
|
|
|
|
moduleObject,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lazyComponent._status = Resolved;
|
|
|
|
|
lazyComponent._result = defaultExport;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
error => {
|
|
|
|
|
if (lazyComponent._status === Pending) {
|
|
|
|
|
lazyComponent._status = Rejected;
|
|
|
|
|
lazyComponent._result = error;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|