Skip to content

Commit 95a313e

Browse files
sebmarkbageAndrew Clark
authored and
Andrew Clark
committed
Unfork Lazy Component Branches (#13902)
* Introduce elementType field This will be used to store the wrapped type of an element. E.g. pure and lazy. The existing type field will be used for the unwrapped type within them. * Store the unwrapped type on the type field of lazy components * Use the raw tags for lazy components Instead, we check if the elementType and type are equal to test if we need to resolve props. This is slightly slower in the normal case but will yield less code and branching. * Clean up lazy branches * Collapse work tag numbering * Split IndeterminateComponent out from Lazy This way we don't have to check the type in a hacky way in the indeterminate path. Also, lets us deal with lazy that resolves to indeterminate and such. * Missing clean up in rebase
1 parent e16cdd5 commit 95a313e

18 files changed

+255
-359
lines changed

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

+1-5
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ import {findCurrentFiberUsingSlowPath} from 'react-reconciler/reflection';
1111
import * as ReactInstanceMap from 'shared/ReactInstanceMap';
1212
import {
1313
ClassComponent,
14-
ClassComponentLazy,
1514
FunctionComponent,
16-
FunctionComponentLazy,
1715
HostComponent,
1816
HostText,
1917
} from 'shared/ReactWorkTags';
@@ -92,9 +90,7 @@ function findAllInRenderedFiberTreeInternal(fiber, test) {
9290
node.tag === HostComponent ||
9391
node.tag === HostText ||
9492
node.tag === ClassComponent ||
95-
node.tag === ClassComponentLazy ||
96-
node.tag === FunctionComponent ||
97-
node.tag === FunctionComponentLazy
93+
node.tag === FunctionComponent
9894
) {
9995
const publicInst = node.stateNode;
10096
if (test(publicInst)) {

packages/react-reconciler/src/ReactChildFiber.js

+4-7
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
import {
2424
FunctionComponent,
2525
ClassComponent,
26-
ClassComponentLazy,
2726
HostText,
2827
HostPortal,
2928
Fragment,
@@ -138,8 +137,7 @@ function coerceRef(
138137
if (owner) {
139138
const ownerFiber = ((owner: any): Fiber);
140139
invariant(
141-
ownerFiber.tag === ClassComponent ||
142-
ownerFiber.tag === ClassComponentLazy,
140+
ownerFiber.tag === ClassComponent,
143141
'Function components cannot have refs.',
144142
);
145143
inst = ownerFiber.stateNode;
@@ -379,7 +377,7 @@ function ChildReconciler(shouldTrackSideEffects) {
379377
element: ReactElement,
380378
expirationTime: ExpirationTime,
381379
): Fiber {
382-
if (current !== null && current.type === element.type) {
380+
if (current !== null && current.elementType === element.type) {
383381
// Move based on index
384382
const existing = useFiber(current, element.props, expirationTime);
385383
existing.ref = coerceRef(returnFiber, current, element);
@@ -1122,7 +1120,7 @@ function ChildReconciler(shouldTrackSideEffects) {
11221120
if (
11231121
child.tag === Fragment
11241122
? element.type === REACT_FRAGMENT_TYPE
1125-
: child.type === element.type
1123+
: child.elementType === element.type
11261124
) {
11271125
deleteRemainingChildren(returnFiber, child.sibling);
11281126
const existing = useFiber(
@@ -1309,8 +1307,7 @@ function ChildReconciler(shouldTrackSideEffects) {
13091307
// component, throw an error. If Fiber return types are disabled,
13101308
// we already threw above.
13111309
switch (returnFiber.tag) {
1312-
case ClassComponent:
1313-
case ClassComponentLazy: {
1310+
case ClassComponent: {
13141311
if (__DEV__) {
13151312
const instance = returnFiber.stateNode;
13161313
if (instance.render._isMockFunction) {

packages/react-reconciler/src/ReactCurrentFiber.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@ import ReactSharedInternals from 'shared/ReactSharedInternals';
1111
import {
1212
IndeterminateComponent,
1313
FunctionComponent,
14-
FunctionComponentLazy,
1514
ClassComponent,
16-
ClassComponentLazy,
1715
HostComponent,
1816
Mode,
17+
LazyComponent,
1918
} from 'shared/ReactWorkTags';
2019
import describeComponentFrame from 'shared/describeComponentFrame';
2120
import getComponentName from 'shared/getComponentName';
@@ -29,10 +28,9 @@ type LifeCyclePhase = 'render' | 'getChildContext';
2928
function describeFiber(fiber: Fiber): string {
3029
switch (fiber.tag) {
3130
case IndeterminateComponent:
31+
case LazyComponent:
3232
case FunctionComponent:
33-
case FunctionComponentLazy:
3433
case ClassComponent:
35-
case ClassComponentLazy:
3634
case HostComponent:
3735
case Mode:
3836
const owner = fiber._debugOwner;

packages/react-reconciler/src/ReactFiber.js

+92-25
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,9 @@ import {
3434
ContextConsumer,
3535
Profiler,
3636
SuspenseComponent,
37-
FunctionComponentLazy,
38-
ClassComponentLazy,
39-
ForwardRefLazy,
37+
FunctionComponent,
4038
PureComponent,
41-
PureComponentLazy,
39+
LazyComponent,
4240
} from 'shared/ReactWorkTags';
4341
import getComponentName from 'shared/getComponentName';
4442

@@ -101,7 +99,11 @@ export type Fiber = {|
10199
// Unique identifier of this child.
102100
key: null | string,
103101

104-
// The function/class/module associated with this fiber.
102+
// The value of element.type which is used to preserve the identity during
103+
// reconciliation of this child.
104+
elementType: any,
105+
106+
// The resolved function/class/ associated with this fiber.
105107
type: any,
106108

107109
// The local state associated with this fiber.
@@ -219,6 +221,7 @@ function FiberNode(
219221
// Instance
220222
this.tag = tag;
221223
this.key = key;
224+
this.elementType = null;
222225
this.type = null;
223226
this.stateNode = null;
224227

@@ -301,16 +304,14 @@ export function resolveLazyComponentTag(
301304
Component: Function,
302305
): WorkTag {
303306
if (typeof Component === 'function') {
304-
return shouldConstruct(Component)
305-
? ClassComponentLazy
306-
: FunctionComponentLazy;
307+
return shouldConstruct(Component) ? ClassComponent : FunctionComponent;
307308
} else if (Component !== undefined && Component !== null) {
308309
const $$typeof = Component.$$typeof;
309310
if ($$typeof === REACT_FORWARD_REF_TYPE) {
310-
return ForwardRefLazy;
311+
return ForwardRef;
311312
}
312313
if ($$typeof === REACT_PURE_TYPE) {
313-
return PureComponentLazy;
314+
return PureComponent;
314315
}
315316
}
316317
return IndeterminateComponent;
@@ -335,6 +336,7 @@ export function createWorkInProgress(
335336
current.key,
336337
current.mode,
337338
);
339+
workInProgress.elementType = current.elementType;
338340
workInProgress.type = current.type;
339341
workInProgress.stateNode = current.stateNode;
340342

@@ -404,7 +406,7 @@ export function createHostRootFiber(isConcurrent: boolean): Fiber {
404406
return createFiber(HostRoot, null, null, mode);
405407
}
406408

407-
export function createFiberFromElement(
409+
function createFiberFromElementWithoutDebugInfo(
408410
element: ReactElement,
409411
mode: TypeOfMode,
410412
expirationTime: ExpirationTime,
@@ -419,9 +421,13 @@ export function createFiberFromElement(
419421
const key = element.key;
420422
const pendingProps = element.props;
421423

422-
let fiberTag;
424+
let fiberTag = IndeterminateComponent;
425+
// The resolved type is set if we know what the final type will be. I.e. it's not lazy.
426+
let resolvedType = type;
423427
if (typeof type === 'function') {
424-
fiberTag = shouldConstruct(type) ? ClassComponent : IndeterminateComponent;
428+
if (shouldConstruct(type)) {
429+
fiberTag = ClassComponent;
430+
}
425431
} else if (typeof type === 'string') {
426432
fiberTag = HostComponent;
427433
} else {
@@ -434,18 +440,23 @@ export function createFiberFromElement(
434440
key,
435441
);
436442
case REACT_CONCURRENT_MODE_TYPE:
437-
fiberTag = Mode;
438-
mode |= ConcurrentMode | StrictMode;
439-
break;
443+
return createFiberFromMode(
444+
pendingProps,
445+
mode | ConcurrentMode | StrictMode,
446+
expirationTime,
447+
key,
448+
);
440449
case REACT_STRICT_MODE_TYPE:
441-
fiberTag = Mode;
442-
mode |= StrictMode;
443-
break;
450+
return createFiberFromMode(
451+
pendingProps,
452+
mode | StrictMode,
453+
expirationTime,
454+
key,
455+
);
444456
case REACT_PROFILER_TYPE:
445457
return createFiberFromProfiler(pendingProps, mode, expirationTime, key);
446458
case REACT_SUSPENSE_TYPE:
447-
fiberTag = SuspenseComponent;
448-
break;
459+
return createFiberFromSuspense(pendingProps, mode, expirationTime, key);
449460
default: {
450461
if (typeof type === 'object' && type !== null) {
451462
switch (type.$$typeof) {
@@ -463,7 +474,8 @@ export function createFiberFromElement(
463474
fiberTag = PureComponent;
464475
break getTag;
465476
case REACT_LAZY_TYPE:
466-
fiberTag = IndeterminateComponent;
477+
fiberTag = LazyComponent;
478+
resolvedType = null;
467479
break getTag;
468480
}
469481
}
@@ -498,14 +510,27 @@ export function createFiberFromElement(
498510
}
499511

500512
fiber = createFiber(fiberTag, pendingProps, key, mode);
501-
fiber.type = type;
513+
fiber.elementType = type;
514+
fiber.type = resolvedType;
502515
fiber.expirationTime = expirationTime;
503516

517+
return fiber;
518+
}
519+
520+
export function createFiberFromElement(
521+
element: ReactElement,
522+
mode: TypeOfMode,
523+
expirationTime: ExpirationTime,
524+
): Fiber {
525+
const fiber = createFiberFromElementWithoutDebugInfo(
526+
element,
527+
mode,
528+
expirationTime,
529+
);
504530
if (__DEV__) {
505531
fiber._debugSource = element._source;
506532
fiber._debugOwner = element._owner;
507533
}
508-
509534
return fiber;
510535
}
511536

@@ -520,7 +545,7 @@ export function createFiberFromFragment(
520545
return fiber;
521546
}
522547

523-
export function createFiberFromProfiler(
548+
function createFiberFromProfiler(
524549
pendingProps: any,
525550
mode: TypeOfMode,
526551
expirationTime: ExpirationTime,
@@ -539,12 +564,51 @@ export function createFiberFromProfiler(
539564
}
540565

541566
const fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode);
567+
// TODO: The Profiler fiber shouldn't have a type. It has a tag.
568+
fiber.elementType = REACT_PROFILER_TYPE;
542569
fiber.type = REACT_PROFILER_TYPE;
543570
fiber.expirationTime = expirationTime;
544571

545572
return fiber;
546573
}
547574

575+
function createFiberFromMode(
576+
pendingProps: any,
577+
mode: TypeOfMode,
578+
expirationTime: ExpirationTime,
579+
key: null | string,
580+
): Fiber {
581+
const fiber = createFiber(Mode, pendingProps, key, mode);
582+
583+
// TODO: The Mode fiber shouldn't have a type. It has a tag.
584+
const type =
585+
(mode & ConcurrentMode) === NoContext
586+
? REACT_STRICT_MODE_TYPE
587+
: REACT_CONCURRENT_MODE_TYPE;
588+
fiber.elementType = type;
589+
fiber.type = type;
590+
591+
fiber.expirationTime = expirationTime;
592+
return fiber;
593+
}
594+
595+
export function createFiberFromSuspense(
596+
pendingProps: any,
597+
mode: TypeOfMode,
598+
expirationTime: ExpirationTime,
599+
key: null | string,
600+
) {
601+
const fiber = createFiber(SuspenseComponent, pendingProps, key, mode);
602+
603+
// TODO: The SuspenseComponent fiber shouldn't have a type. It has a tag.
604+
const type = REACT_SUSPENSE_TYPE;
605+
fiber.elementType = type;
606+
fiber.type = type;
607+
608+
fiber.expirationTime = expirationTime;
609+
return fiber;
610+
}
611+
548612
export function createFiberFromText(
549613
content: string,
550614
mode: TypeOfMode,
@@ -557,6 +621,8 @@ export function createFiberFromText(
557621

558622
export function createFiberFromHostInstanceForDeletion(): Fiber {
559623
const fiber = createFiber(HostComponent, null, null, NoContext);
624+
// TODO: These should not need a type.
625+
fiber.elementType = 'DELETED';
560626
fiber.type = 'DELETED';
561627
return fiber;
562628
}
@@ -596,6 +662,7 @@ export function assignFiberPropertiesInDEV(
596662

597663
target.tag = source.tag;
598664
target.key = source.key;
665+
target.elementType = source.elementType;
599666
target.type = source.type;
600667
target.stateNode = source.stateNode;
601668
target.return = source.return;

0 commit comments

Comments
 (0)