Skip to content

Commit 5031ebf

Browse files
authored
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
1 parent 77b7a66 commit 5031ebf

24 files changed

+882
-208
lines changed

packages/react-dom/src/test-utils/ReactTestUtils.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import {findCurrentFiberUsingSlowPath} from 'react-reconciler/reflection';
1111
import * as ReactInstanceMap from 'shared/ReactInstanceMap';
1212
import {
1313
ClassComponent,
14+
ClassComponentLazy,
1415
FunctionalComponent,
16+
FunctionalComponentLazy,
1517
HostComponent,
1618
HostText,
1719
} from 'shared/ReactTypeOfWork';
@@ -81,7 +83,9 @@ function findAllInRenderedFiberTreeInternal(fiber, test) {
8183
node.tag === HostComponent ||
8284
node.tag === HostText ||
8385
node.tag === ClassComponent ||
84-
node.tag === FunctionalComponent
86+
node.tag === ClassComponentLazy ||
87+
node.tag === FunctionalComponent ||
88+
node.tag === FunctionalComponentLazy
8589
) {
8690
const publicInst = node.stateNode;
8791
if (test(publicInst)) {

packages/react-reconciler/src/ReactChildFiber.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
import {
2424
FunctionalComponent,
2525
ClassComponent,
26+
ClassComponentLazy,
2627
HostText,
2728
HostPortal,
2829
Fragment,
@@ -117,7 +118,7 @@ function coerceRef(
117118
if (!didWarnAboutStringRefInStrictMode[componentName]) {
118119
warningWithoutStack(
119120
false,
120-
'A string ref, "%s", has been found within a strict mode tree. ' +
121+
'A string ref, "%s", has been found within a strict mode tree. ' +
121122
'String refs are a source of potential bugs and should be avoided. ' +
122123
'We recommend using createRef() instead.' +
123124
'\n%s' +
@@ -137,7 +138,8 @@ function coerceRef(
137138
if (owner) {
138139
const ownerFiber = ((owner: any): Fiber);
139140
invariant(
140-
ownerFiber.tag === ClassComponent,
141+
ownerFiber.tag === ClassComponent ||
142+
ownerFiber.tag === ClassComponentLazy,
141143
'Stateless function components cannot have refs.',
142144
);
143145
inst = ownerFiber.stateNode;
@@ -1307,7 +1309,8 @@ function ChildReconciler(shouldTrackSideEffects) {
13071309
// component, throw an error. If Fiber return types are disabled,
13081310
// we already threw above.
13091311
switch (returnFiber.tag) {
1310-
case ClassComponent: {
1312+
case ClassComponent:
1313+
case ClassComponentLazy: {
13111314
if (__DEV__) {
13121315
const instance = returnFiber.stateNode;
13131316
if (instance.render._isMockFunction) {

packages/react-reconciler/src/ReactCurrentFiber.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import ReactSharedInternals from 'shared/ReactSharedInternals';
1111
import {
1212
IndeterminateComponent,
1313
FunctionalComponent,
14+
FunctionalComponentLazy,
1415
ClassComponent,
16+
ClassComponentLazy,
1517
HostComponent,
1618
Mode,
1719
} from 'shared/ReactTypeOfWork';
@@ -28,7 +30,9 @@ function describeFiber(fiber: Fiber): string {
2830
switch (fiber.tag) {
2931
case IndeterminateComponent:
3032
case FunctionalComponent:
33+
case FunctionalComponentLazy:
3134
case ClassComponent:
35+
case ClassComponentLazy:
3236
case HostComponent:
3337
case Mode:
3438
const owner = fiber._debugOwner;

packages/react-reconciler/src/ReactFiber.js

Lines changed: 79 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import {
3333
ContextConsumer,
3434
Profiler,
3535
PlaceholderComponent,
36+
FunctionalComponentLazy,
37+
ClassComponentLazy,
38+
ForwardRefLazy,
3639
} from 'shared/ReactTypeOfWork';
3740
import getComponentName from 'shared/getComponentName';
3841

@@ -278,8 +281,32 @@ const createFiber = function(
278281
return new FiberNode(tag, pendingProps, key, mode);
279282
};
280283

281-
function shouldConstruct(Component) {
282-
return !!(Component.prototype && Component.prototype.isReactComponent);
284+
function shouldConstruct(Component: Function) {
285+
const prototype = Component.prototype;
286+
return (
287+
typeof prototype === 'object' &&
288+
prototype !== null &&
289+
typeof prototype.isReactComponent === 'object' &&
290+
prototype.isReactComponent !== null
291+
);
292+
}
293+
294+
export function resolveLazyComponentTag(
295+
fiber: Fiber,
296+
Component: Function,
297+
): void {
298+
if (typeof Component === 'function') {
299+
return shouldConstruct(Component)
300+
? ClassComponentLazy
301+
: FunctionalComponentLazy;
302+
} else if (
303+
Component !== undefined &&
304+
Component !== null &&
305+
Component.$$typeof
306+
) {
307+
return ForwardRefLazy;
308+
}
309+
return IndeterminateComponent;
283310
}
284311

285312
// This is used to create an alternate fiber to do work on.
@@ -390,15 +417,15 @@ export function createFiberFromElement(
390417
let fiber;
391418
const type = element.type;
392419
const key = element.key;
393-
let pendingProps = element.props;
420+
const pendingProps = element.props;
394421

395422
let fiberTag;
396423
if (typeof type === 'function') {
397424
fiberTag = shouldConstruct(type) ? ClassComponent : IndeterminateComponent;
398425
} else if (typeof type === 'string') {
399426
fiberTag = HostComponent;
400427
} else {
401-
switch (type) {
428+
getTag: switch (type) {
402429
case REACT_FRAGMENT_TYPE:
403430
return createFiberFromFragment(
404431
pendingProps.children,
@@ -419,9 +446,54 @@ export function createFiberFromElement(
419446
case REACT_PLACEHOLDER_TYPE:
420447
fiberTag = PlaceholderComponent;
421448
break;
422-
default:
423-
fiberTag = getFiberTagFromObjectType(type, owner);
424-
break;
449+
default: {
450+
if (typeof type === 'object' && type !== null) {
451+
switch (type.$$typeof) {
452+
case REACT_PROVIDER_TYPE:
453+
fiberTag = ContextProvider;
454+
break getTag;
455+
case REACT_CONTEXT_TYPE:
456+
// This is a consumer
457+
fiberTag = ContextConsumer;
458+
break getTag;
459+
case REACT_FORWARD_REF_TYPE:
460+
fiberTag = ForwardRef;
461+
break getTag;
462+
default: {
463+
if (typeof type.then === 'function') {
464+
fiberTag = IndeterminateComponent;
465+
break getTag;
466+
}
467+
}
468+
}
469+
}
470+
let info = '';
471+
if (__DEV__) {
472+
if (
473+
type === undefined ||
474+
(typeof type === 'object' &&
475+
type !== null &&
476+
Object.keys(type).length === 0)
477+
) {
478+
info +=
479+
' You likely forgot to export your component from the file ' +
480+
"it's defined in, or you might have mixed up default and " +
481+
'named imports.';
482+
}
483+
const ownerName = owner ? getComponentName(owner.type) : null;
484+
if (ownerName) {
485+
info += '\n\nCheck the render method of `' + ownerName + '`.';
486+
}
487+
}
488+
invariant(
489+
false,
490+
'Element type is invalid: expected a string (for built-in ' +
491+
'components) or a class/function (for composite components) ' +
492+
'but got: %s.%s',
493+
type == null ? type : typeof type,
494+
info,
495+
);
496+
}
425497
}
426498
}
427499

@@ -437,49 +509,6 @@ export function createFiberFromElement(
437509
return fiber;
438510
}
439511

440-
function getFiberTagFromObjectType(type, owner): TypeOfWork {
441-
const $$typeof =
442-
typeof type === 'object' && type !== null ? type.$$typeof : null;
443-
444-
switch ($$typeof) {
445-
case REACT_PROVIDER_TYPE:
446-
return ContextProvider;
447-
case REACT_CONTEXT_TYPE:
448-
// This is a consumer
449-
return ContextConsumer;
450-
case REACT_FORWARD_REF_TYPE:
451-
return ForwardRef;
452-
default: {
453-
let info = '';
454-
if (__DEV__) {
455-
if (
456-
type === undefined ||
457-
(typeof type === 'object' &&
458-
type !== null &&
459-
Object.keys(type).length === 0)
460-
) {
461-
info +=
462-
' You likely forgot to export your component from the file ' +
463-
"it's defined in, or you might have mixed up default and " +
464-
'named imports.';
465-
}
466-
const ownerName = owner ? getComponentName(owner.type) : null;
467-
if (ownerName) {
468-
info += '\n\nCheck the render method of `' + ownerName + '`.';
469-
}
470-
}
471-
invariant(
472-
false,
473-
'Element type is invalid: expected a string (for built-in ' +
474-
'components) or a class/function (for composite components) ' +
475-
'but got: %s.%s',
476-
type == null ? type : typeof type,
477-
info,
478-
);
479-
}
480-
}
481-
}
482-
483512
export function createFiberFromFragment(
484513
elements: ReactFragment,
485514
mode: TypeOfMode,

0 commit comments

Comments
 (0)