Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d9659e4

Browse files
author
Andrew Clark
authored
Lazy components must use React.lazy (facebook#13885)
Removes support for using arbitrary promises as the type of a React element. Instead, promises must be wrapped in React.lazy. This gives us flexibility later if we need to change the protocol. The reason is that promises do not provide a way to call their constructor multiple times. For example: const promiseForA = new Promise(resolve => { fetchA(a => resolve(a)); }); Given a reference to `promiseForA`, there's no way to call `fetchA` again. Calling `then` on the promise doesn't run the constructor again; it only attaches another listener. In the future we will likely introduce an API like `React.eager` that is similar to `lazy` but eagerly calls the constructor. That gives us the ability to call the constructor multiple times. E.g. to increase the priority, or to retry if the first operation failed.
1 parent 0648ca6 commit d9659e4

18 files changed

+416
-393
lines changed

packages/react-dom/src/__tests__/ReactServerRendering-test.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -582,11 +582,6 @@ describe('ReactDOMServer', () => {
582582
);
583583
ReactDOMServer.renderToString(<LazyFoo />);
584584
}).toThrow('ReactDOMServer does not yet support lazy-loaded components.');
585-
586-
expect(() => {
587-
const FooPromise = {then() {}};
588-
ReactDOMServer.renderToString(<FooPromise />);
589-
}).toThrow('ReactDOMServer does not yet support lazy-loaded components.');
590585
});
591586

592587
it('should throw (in dev) when children are mutated during render', () => {

packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -444,15 +444,18 @@ describe('ReactDOMServerHydration', () => {
444444
});
445445

446446
it('should be able to use lazy components after hydrating', async () => {
447-
const Lazy = new Promise(resolve => {
448-
setTimeout(
449-
() =>
450-
resolve(function World() {
451-
return 'world';
452-
}),
453-
1000,
454-
);
455-
});
447+
const Lazy = React.lazy(
448+
() =>
449+
new Promise(resolve => {
450+
setTimeout(
451+
() =>
452+
resolve(function World() {
453+
return 'world';
454+
}),
455+
1000,
456+
);
457+
}),
458+
);
456459
class HelloWorld extends React.Component {
457460
state = {isClient: false};
458461
componentDidMount() {

packages/react-dom/src/server/ReactPartialRenderer.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
REACT_PROFILER_TYPE,
3939
REACT_PROVIDER_TYPE,
4040
REACT_CONTEXT_TYPE,
41+
REACT_LAZY_TYPE,
4142
} from 'shared/ReactSymbols';
4243

4344
import {
@@ -1005,14 +1006,11 @@ class ReactDOMServerRenderer {
10051006
this.stack.push(frame);
10061007
return '';
10071008
}
1008-
default:
1009-
if (typeof elementType.then === 'function') {
1010-
invariant(
1011-
false,
1012-
'ReactDOMServer does not yet support lazy-loaded components.',
1013-
);
1014-
}
1015-
break;
1009+
case REACT_LAZY_TYPE:
1010+
invariant(
1011+
false,
1012+
'ReactDOMServer does not yet support lazy-loaded components.',
1013+
);
10161014
}
10171015
}
10181016

packages/react-reconciler/src/ReactFiber.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import {
6060
REACT_CONCURRENT_MODE_TYPE,
6161
REACT_SUSPENSE_TYPE,
6262
REACT_PURE_TYPE,
63+
REACT_LAZY_TYPE,
6364
} from 'shared/ReactSymbols';
6465

6566
let hasBadMapPolyfill;
@@ -461,12 +462,9 @@ export function createFiberFromElement(
461462
case REACT_PURE_TYPE:
462463
fiberTag = PureComponent;
463464
break getTag;
464-
default: {
465-
if (typeof type.then === 'function') {
466-
fiberTag = IndeterminateComponent;
467-
break getTag;
468-
}
469-
}
465+
case REACT_LAZY_TYPE:
466+
fiberTag = IndeterminateComponent;
467+
break getTag;
470468
}
471469
}
472470
let info = '';

packages/react-reconciler/src/ReactFiberBeginWork.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,13 @@ import {
104104
updateClassInstance,
105105
} from './ReactFiberClassComponent';
106106
import {readLazyComponentType} from './ReactFiberLazyComponent';
107-
import {getResultFromResolvedThenable} from 'shared/ReactLazyComponent';
107+
import {getResultFromResolvedLazyComponent} from 'shared/ReactLazyComponent';
108108
import {
109109
resolveLazyComponentTag,
110110
createFiberFromFragment,
111111
createWorkInProgress,
112112
} from './ReactFiber';
113+
import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';
113114

114115
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
115116

@@ -728,7 +729,7 @@ function mountIndeterminateComponent(
728729
if (
729730
typeof Component === 'object' &&
730731
Component !== null &&
731-
typeof Component.then === 'function'
732+
Component.$$typeof === REACT_LAZY_TYPE
732733
) {
733734
// We can't start a User Timing measurement with correct label yet.
734735
// Cancel and resume right after we know the tag.
@@ -1422,7 +1423,7 @@ function beginWork(
14221423
}
14231424
case ClassComponentLazy: {
14241425
const thenable = workInProgress.type;
1425-
const Component = getResultFromResolvedThenable(thenable);
1426+
const Component = getResultFromResolvedLazyComponent(thenable);
14261427
if (isLegacyContextProvider(Component)) {
14271428
pushLegacyContextProvider(workInProgress);
14281429
}
@@ -1498,7 +1499,7 @@ function beginWork(
14981499
}
14991500
case FunctionComponentLazy: {
15001501
const thenable = workInProgress.type;
1501-
const Component = getResultFromResolvedThenable(thenable);
1502+
const Component = getResultFromResolvedLazyComponent(thenable);
15021503
const unresolvedProps = workInProgress.pendingProps;
15031504
const child = updateFunctionComponent(
15041505
current,
@@ -1523,7 +1524,7 @@ function beginWork(
15231524
}
15241525
case ClassComponentLazy: {
15251526
const thenable = workInProgress.type;
1526-
const Component = getResultFromResolvedThenable(thenable);
1527+
const Component = getResultFromResolvedLazyComponent(thenable);
15271528
const unresolvedProps = workInProgress.pendingProps;
15281529
const child = updateClassComponent(
15291530
current,
@@ -1565,7 +1566,7 @@ function beginWork(
15651566
}
15661567
case ForwardRefLazy: {
15671568
const thenable = workInProgress.type;
1568-
const Component = getResultFromResolvedThenable(thenable);
1569+
const Component = getResultFromResolvedLazyComponent(thenable);
15691570
const unresolvedProps = workInProgress.pendingProps;
15701571
const child = updateForwardRef(
15711572
current,
@@ -1608,7 +1609,7 @@ function beginWork(
16081609
}
16091610
case PureComponentLazy: {
16101611
const thenable = workInProgress.type;
1611-
const Component = getResultFromResolvedThenable(thenable);
1612+
const Component = getResultFromResolvedLazyComponent(thenable);
16121613
const unresolvedProps = workInProgress.pendingProps;
16131614
const child = updatePureComponent(
16141615
current,

packages/react-reconciler/src/ReactFiberCompleteWork.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import {
4242
} from 'shared/ReactWorkTags';
4343
import {Placement, Ref, Update} from 'shared/ReactSideEffectTags';
4444
import invariant from 'shared/invariant';
45-
import {getResultFromResolvedThenable} from 'shared/ReactLazyComponent';
45+
import {getResultFromResolvedLazyComponent} from 'shared/ReactLazyComponent';
4646

4747
import {
4848
createInstance,
@@ -552,7 +552,7 @@ function completeWork(
552552
break;
553553
}
554554
case ClassComponentLazy: {
555-
const Component = getResultFromResolvedThenable(workInProgress.type);
555+
const Component = getResultFromResolvedLazyComponent(workInProgress.type);
556556
if (isLegacyContextProvider(Component)) {
557557
popLegacyContext(workInProgress);
558558
}

packages/react-reconciler/src/ReactFiberContext.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import getComponentName from 'shared/getComponentName';
2020
import invariant from 'shared/invariant';
2121
import warningWithoutStack from 'shared/warningWithoutStack';
2222
import checkPropTypes from 'prop-types/checkPropTypes';
23-
import {getResultFromResolvedThenable} from 'shared/ReactLazyComponent';
23+
import {getResultFromResolvedLazyComponent} from 'shared/ReactLazyComponent';
2424

2525
import * as ReactCurrentFiber from './ReactCurrentFiber';
2626
import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
@@ -298,7 +298,7 @@ function findCurrentUnmaskedContext(fiber: Fiber): Object {
298298
break;
299299
}
300300
case ClassComponentLazy: {
301-
const Component = getResultFromResolvedThenable(node.type);
301+
const Component = getResultFromResolvedLazyComponent(node.type);
302302
if (isContextProvider(Component)) {
303303
return node.stateNode.__reactInternalMemoizedMergedChildContext;
304304
}

packages/react-reconciler/src/ReactFiberLazyComponent.js

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,28 @@
77
* @flow
88
*/
99

10-
import type {Thenable} from 'shared/ReactLazyComponent';
10+
import type {LazyComponent} from 'shared/ReactLazyComponent';
1111

1212
import {Resolved, Rejected, Pending} from 'shared/ReactLazyComponent';
1313

14-
export function readLazyComponentType<T>(thenable: Thenable<T>): T {
15-
const status = thenable._reactStatus;
14+
export function readLazyComponentType<T>(lazyComponent: LazyComponent<T>): T {
15+
const status = lazyComponent._status;
1616
switch (status) {
1717
case Resolved:
18-
const Component: T = thenable._reactResult;
18+
const Component: T = lazyComponent._result;
1919
return Component;
2020
case Rejected:
21-
throw thenable._reactResult;
21+
throw lazyComponent._result;
2222
case Pending:
23-
throw thenable;
23+
throw lazyComponent;
2424
default: {
25-
thenable._reactStatus = Pending;
25+
lazyComponent._status = Pending;
26+
const ctor = lazyComponent._ctor;
27+
const thenable = ctor();
2628
thenable.then(
2729
resolvedValue => {
28-
if (thenable._reactStatus === Pending) {
29-
thenable._reactStatus = Resolved;
30+
if (lazyComponent._status === Pending) {
31+
lazyComponent._status = Resolved;
3032
if (typeof resolvedValue === 'object' && resolvedValue !== null) {
3133
// If the `default` property is not empty, assume it's the result
3234
// of an async import() and use that. Otherwise, use the
@@ -39,13 +41,13 @@ export function readLazyComponentType<T>(thenable: Thenable<T>): T {
3941
} else {
4042
resolvedValue = resolvedValue;
4143
}
42-
thenable._reactResult = resolvedValue;
44+
lazyComponent._result = resolvedValue;
4345
}
4446
},
4547
error => {
46-
if (thenable._reactStatus === Pending) {
47-
thenable._reactStatus = Rejected;
48-
thenable._reactResult = error;
48+
if (lazyComponent._status === Pending) {
49+
lazyComponent._status = Rejected;
50+
lazyComponent._result = error;
4951
}
5052
},
5153
);

packages/react-reconciler/src/ReactFiberReconciler.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {
3131
import getComponentName from 'shared/getComponentName';
3232
import invariant from 'shared/invariant';
3333
import warningWithoutStack from 'shared/warningWithoutStack';
34-
import {getResultFromResolvedThenable} from 'shared/ReactLazyComponent';
34+
import {getResultFromResolvedLazyComponent} from 'shared/ReactLazyComponent';
3535

3636
import {getPublicInstance} from './ReactFiberHostConfig';
3737
import {
@@ -107,7 +107,7 @@ function getContextForSubtree(
107107
return processChildContext(fiber, Component, parentContext);
108108
}
109109
} else if (fiber.tag === ClassComponentLazy) {
110-
const Component = getResultFromResolvedThenable(fiber.type);
110+
const Component = getResultFromResolvedLazyComponent(fiber.type);
111111
if (isLegacyContextProvider(Component)) {
112112
return processChildContext(fiber, Component, parentContext);
113113
}

packages/react-reconciler/src/ReactFiberScheduler.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ import {
5353
import getComponentName from 'shared/getComponentName';
5454
import invariant from 'shared/invariant';
5555
import warningWithoutStack from 'shared/warningWithoutStack';
56-
import {getResultFromResolvedThenable} from 'shared/ReactLazyComponent';
56+
import {getResultFromResolvedLazyComponent} from 'shared/ReactLazyComponent';
5757

5858
import {
5959
scheduleTimeout,
@@ -312,7 +312,9 @@ if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
312312
break;
313313
}
314314
case ClassComponentLazy: {
315-
const Component = getResultFromResolvedThenable(failedUnitOfWork.type);
315+
const Component = getResultFromResolvedLazyComponent(
316+
failedUnitOfWork.type,
317+
);
316318
if (isLegacyContextProvider(Component)) {
317319
popLegacyContext(failedUnitOfWork);
318320
}

0 commit comments

Comments
 (0)