Skip to content

Commit 1becfaf

Browse files
committed
Refactor: Add resolveClassComponentProps
There are a few places in the reconciler where we modify the fiber's internal props object before passing it to userspace. The trickiest one is class components, because the props object gets exposed in many different places, including as a property on the class instance. This was already accounted for when we added support for setting default props on a lazy wrapper (i.e. React.lazy that resolves to a class component). In all of these same places, we will also need to remove the ref prop when enableRefAsProp is on. As a first step, this adds a new function, resolveClassComponentProps, where both default prop resolution and ref prop removal will happen.
1 parent e3afd02 commit 1becfaf

File tree

4 files changed

+67
-35
lines changed

4 files changed

+67
-35
lines changed

packages/react-reconciler/src/ReactFiberBeginWork.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ import {
239239
mountClassInstance,
240240
resumeMountClassInstance,
241241
updateClassInstance,
242+
resolveClassComponentProps,
242243
} from './ReactFiberClassComponent';
243244
import {resolveDefaultProps} from './ReactFiberLazyComponent';
244245
import {
@@ -1698,10 +1699,10 @@ function mountLazyComponent(
16981699
// Store the unwrapped component in the type.
16991700
workInProgress.type = Component;
17001701
const resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component));
1701-
const resolvedProps = resolveDefaultProps(Component, props);
17021702
let child;
17031703
switch (resolvedTag) {
17041704
case FunctionComponent: {
1705+
const resolvedProps = resolveDefaultProps(Component, props);
17051706
if (__DEV__) {
17061707
validateFunctionComponentInDev(workInProgress, Component);
17071708
workInProgress.type = Component =
@@ -1717,6 +1718,7 @@ function mountLazyComponent(
17171718
return child;
17181719
}
17191720
case ClassComponent: {
1721+
const resolvedProps = resolveClassComponentProps(Component, props, false);
17201722
if (__DEV__) {
17211723
workInProgress.type = Component =
17221724
resolveClassForHotReloading(Component);
@@ -1731,6 +1733,7 @@ function mountLazyComponent(
17311733
return child;
17321734
}
17331735
case ForwardRef: {
1736+
const resolvedProps = resolveDefaultProps(Component, props);
17341737
if (__DEV__) {
17351738
workInProgress.type = Component =
17361739
resolveForwardRefForHotReloading(Component);
@@ -1745,6 +1748,7 @@ function mountLazyComponent(
17451748
return child;
17461749
}
17471750
case MemoComponent: {
1751+
const resolvedProps = resolveDefaultProps(Component, props);
17481752
child = updateMemoComponent(
17491753
null,
17501754
workInProgress,
@@ -4007,10 +4011,11 @@ function beginWork(
40074011
case ClassComponent: {
40084012
const Component = workInProgress.type;
40094013
const unresolvedProps = workInProgress.pendingProps;
4010-
const resolvedProps =
4011-
workInProgress.elementType === Component
4012-
? unresolvedProps
4013-
: resolveDefaultProps(Component, unresolvedProps);
4014+
const resolvedProps = resolveClassComponentProps(
4015+
Component,
4016+
unresolvedProps,
4017+
workInProgress.elementType === Component,
4018+
);
40144019
return updateClassComponent(
40154020
current,
40164021
workInProgress,
@@ -4090,10 +4095,11 @@ function beginWork(
40904095
case IncompleteClassComponent: {
40914096
const Component = workInProgress.type;
40924097
const unresolvedProps = workInProgress.pendingProps;
4093-
const resolvedProps =
4094-
workInProgress.elementType === Component
4095-
? unresolvedProps
4096-
: resolveDefaultProps(Component, unresolvedProps);
4098+
const resolvedProps = resolveClassComponentProps(
4099+
Component,
4100+
unresolvedProps,
4101+
workInProgress.elementType === Component,
4102+
);
40974103
return mountIncompleteClassComponent(
40984104
current,
40994105
workInProgress,

packages/react-reconciler/src/ReactFiberClassComponent.js

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import assign from 'shared/assign';
3434
import isArray from 'shared/isArray';
3535
import {REACT_CONTEXT_TYPE, REACT_CONSUMER_TYPE} from 'shared/ReactSymbols';
3636

37-
import {resolveDefaultProps} from './ReactFiberLazyComponent';
3837
import {
3938
DebugTracingMode,
4039
NoMode,
@@ -1056,10 +1055,11 @@ function updateClassInstance(
10561055
cloneUpdateQueue(current, workInProgress);
10571056

10581057
const unresolvedOldProps = workInProgress.memoizedProps;
1059-
const oldProps =
1060-
workInProgress.type === workInProgress.elementType
1061-
? unresolvedOldProps
1062-
: resolveDefaultProps(workInProgress.type, unresolvedOldProps);
1058+
const oldProps = resolveClassComponentProps(
1059+
ctor,
1060+
unresolvedOldProps,
1061+
workInProgress.type === workInProgress.elementType,
1062+
);
10631063
instance.props = oldProps;
10641064
const unresolvedNewProps = workInProgress.pendingProps;
10651065

@@ -1229,6 +1229,34 @@ function updateClassInstance(
12291229
return shouldUpdate;
12301230
}
12311231

1232+
export function resolveClassComponentProps(
1233+
Component: any,
1234+
baseProps: Object,
1235+
// Only resolve default props if this is a lazy component. Otherwise, they
1236+
// would have already been resolved by the JSX runtime.
1237+
// TODO: We're going to remove default prop resolution from the JSX runtime
1238+
// and keep it only for class components. As part of that change, we should
1239+
// remove this extra check.
1240+
alreadyResolvedDefaultProps: boolean,
1241+
): Object {
1242+
let newProps = baseProps;
1243+
1244+
// Resolve default props. Taken from old JSX runtime, where this used to live.
1245+
const defaultProps = Component.defaultProps;
1246+
if (defaultProps && !alreadyResolvedDefaultProps) {
1247+
newProps = assign({}, newProps, baseProps);
1248+
for (const propName in defaultProps) {
1249+
if (newProps[propName] === undefined) {
1250+
newProps[propName] = defaultProps[propName];
1251+
}
1252+
}
1253+
}
1254+
1255+
// TODO: Remove ref from props object
1256+
1257+
return newProps;
1258+
}
1259+
12321260
export {
12331261
constructClassInstance,
12341262
mountClassInstance,

packages/react-reconciler/src/ReactFiberCommitWork.js

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ import {
103103
setCurrentFiber as setCurrentDebugFiberInDEV,
104104
getCurrentFiber as getCurrentDebugFiberInDEV,
105105
} from './ReactCurrentFiber';
106-
import {resolveDefaultProps} from './ReactFiberLazyComponent';
106+
import {resolveClassComponentProps} from './ReactFiberClassComponent';
107107
import {
108108
isCurrentUpdateNested,
109109
getCommitTime,
@@ -470,7 +470,7 @@ function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
470470
// TODO: revisit this when we implement resuming.
471471
if (__DEV__) {
472472
if (
473-
finishedWork.type === finishedWork.elementType &&
473+
!finishedWork.type.defaultProps &&
474474
!didWarnAboutReassigningProps
475475
) {
476476
if (instance.props !== finishedWork.memoizedProps) {
@@ -496,9 +496,11 @@ function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
496496
}
497497
}
498498
const snapshot = instance.getSnapshotBeforeUpdate(
499-
finishedWork.elementType === finishedWork.type
500-
? prevProps
501-
: resolveDefaultProps(finishedWork.type, prevProps),
499+
resolveClassComponentProps(
500+
finishedWork.type,
501+
prevProps,
502+
finishedWork.elementType === finishedWork.type,
503+
),
502504
prevState,
503505
);
504506
if (__DEV__) {
@@ -805,10 +807,7 @@ function commitClassLayoutLifecycles(
805807
// but instead we rely on them being set during last render.
806808
// TODO: revisit this when we implement resuming.
807809
if (__DEV__) {
808-
if (
809-
finishedWork.type === finishedWork.elementType &&
810-
!didWarnAboutReassigningProps
811-
) {
810+
if (!finishedWork.type.defaultProps && !didWarnAboutReassigningProps) {
812811
if (instance.props !== finishedWork.memoizedProps) {
813812
console.error(
814813
'Expected %s props to match memoized props before ' +
@@ -847,19 +846,17 @@ function commitClassLayoutLifecycles(
847846
}
848847
}
849848
} else {
850-
const prevProps =
851-
finishedWork.elementType === finishedWork.type
852-
? current.memoizedProps
853-
: resolveDefaultProps(finishedWork.type, current.memoizedProps);
849+
const prevProps = resolveClassComponentProps(
850+
finishedWork.type,
851+
current.memoizedProps,
852+
finishedWork.elementType === finishedWork.type,
853+
);
854854
const prevState = current.memoizedState;
855855
// We could update instance props and state here,
856856
// but instead we rely on them being set during last render.
857857
// TODO: revisit this when we implement resuming.
858858
if (__DEV__) {
859-
if (
860-
finishedWork.type === finishedWork.elementType &&
861-
!didWarnAboutReassigningProps
862-
) {
859+
if (!finishedWork.type.defaultProps && !didWarnAboutReassigningProps) {
863860
if (instance.props !== finishedWork.memoizedProps) {
864861
console.error(
865862
'Expected %s props to match memoized props before ' +
@@ -916,10 +913,7 @@ function commitClassCallbacks(finishedWork: Fiber) {
916913
if (updateQueue !== null) {
917914
const instance = finishedWork.stateNode;
918915
if (__DEV__) {
919-
if (
920-
finishedWork.type === finishedWork.elementType &&
921-
!didWarnAboutReassigningProps
922-
) {
916+
if (!finishedWork.type.defaultProps && !didWarnAboutReassigningProps) {
923917
if (instance.props !== finishedWork.memoizedProps) {
924918
console.error(
925919
'Expected %s props to match memoized props before ' +

packages/react-reconciler/src/ReactFiberLazyComponent.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
import assign from 'shared/assign';
1111

1212
export function resolveDefaultProps(Component: any, baseProps: Object): Object {
13+
// TODO: Remove support for default props for everything except class
14+
// components, including setting default props on a lazy wrapper around a
15+
// class type.
16+
1317
if (Component && Component.defaultProps) {
1418
// Resolve default props. Taken from ReactElement
1519
const props = assign({}, baseProps);

0 commit comments

Comments
 (0)