Skip to content

Commit bfd8da8

Browse files
authored
Make class prop resolution faster (#28766)
`delete` causes an object (in V8, and maybe other engines) to deopt to a dictionary instead of a class. Instead of `assign` + `delete`, manually iterate over all the properties, like the JSX runtime does. To avoid copying the object twice I moved the `ref` prop removal to come before handling default props. If we already cloned the props to remove `ref`, then we can skip cloning again to handle default props.
1 parent cbb6f2b commit bfd8da8

File tree

2 files changed

+44
-25
lines changed

2 files changed

+44
-25
lines changed

packages/react-reconciler/src/ReactFiberClassComponent.js

+19-12
Original file line numberDiff line numberDiff line change
@@ -1251,32 +1251,39 @@ export function resolveClassComponentProps(
12511251
): Object {
12521252
let newProps = baseProps;
12531253

1254-
// Resolve default props. Taken from old JSX runtime, where this used to live.
1254+
if (enableRefAsProp) {
1255+
// Remove ref from the props object, if it exists.
1256+
if ('ref' in baseProps) {
1257+
newProps = ({}: any);
1258+
for (const propName in baseProps) {
1259+
if (propName !== 'ref') {
1260+
newProps[propName] = baseProps[propName];
1261+
}
1262+
}
1263+
}
1264+
}
1265+
1266+
// Resolve default props.
12551267
const defaultProps = Component.defaultProps;
12561268
if (
12571269
defaultProps &&
12581270
// If disableDefaultPropsExceptForClasses is true, we always resolve
12591271
// default props here in the reconciler, rather than in the JSX runtime.
12601272
(disableDefaultPropsExceptForClasses || !alreadyResolvedDefaultProps)
12611273
) {
1262-
newProps = assign({}, newProps, baseProps);
1274+
// We may have already copied the props object above to remove ref. If so,
1275+
// we can modify that. Otherwise, copy the props object with Object.assign.
1276+
if (newProps === baseProps) {
1277+
newProps = assign({}, newProps, baseProps);
1278+
}
1279+
// Taken from old JSX runtime, where this used to live.
12631280
for (const propName in defaultProps) {
12641281
if (newProps[propName] === undefined) {
12651282
newProps[propName] = defaultProps[propName];
12661283
}
12671284
}
12681285
}
12691286

1270-
if (enableRefAsProp) {
1271-
// Remove ref from the props object, if it exists.
1272-
if ('ref' in newProps) {
1273-
if (newProps === baseProps) {
1274-
newProps = assign({}, newProps);
1275-
}
1276-
delete newProps.ref;
1277-
}
1278-
}
1279-
12801287
return newProps;
12811288
}
12821289

packages/react-server/src/ReactFizzServer.js

+25-13
Original file line numberDiff line numberDiff line change
@@ -1397,24 +1397,36 @@ export function resolveClassComponentProps(
13971397
): Object {
13981398
let newProps = baseProps;
13991399

1400-
// Resolve default props. Taken from old JSX runtime, where this used to live.
1401-
const defaultProps = Component.defaultProps;
1402-
if (defaultProps && disableDefaultPropsExceptForClasses) {
1403-
newProps = assign({}, newProps, baseProps);
1404-
for (const propName in defaultProps) {
1405-
if (newProps[propName] === undefined) {
1406-
newProps[propName] = defaultProps[propName];
1400+
if (enableRefAsProp) {
1401+
// Remove ref from the props object, if it exists.
1402+
if ('ref' in baseProps) {
1403+
newProps = ({}: any);
1404+
for (const propName in baseProps) {
1405+
if (propName !== 'ref') {
1406+
newProps[propName] = baseProps[propName];
1407+
}
14071408
}
14081409
}
14091410
}
14101411

1411-
if (enableRefAsProp) {
1412-
// Remove ref from the props object, if it exists.
1413-
if ('ref' in newProps) {
1414-
if (newProps === baseProps) {
1415-
newProps = assign({}, newProps);
1412+
// Resolve default props.
1413+
const defaultProps = Component.defaultProps;
1414+
if (
1415+
defaultProps &&
1416+
// If disableDefaultPropsExceptForClasses is true, we always resolve
1417+
// default props here, rather than in the JSX runtime.
1418+
disableDefaultPropsExceptForClasses
1419+
) {
1420+
// We may have already copied the props object above to remove ref. If so,
1421+
// we can modify that. Otherwise, copy the props object with Object.assign.
1422+
if (newProps === baseProps) {
1423+
newProps = assign({}, newProps, baseProps);
1424+
}
1425+
// Taken from old JSX runtime, where this used to live.
1426+
for (const propName in defaultProps) {
1427+
if (newProps[propName] === undefined) {
1428+
newProps[propName] = defaultProps[propName];
14161429
}
1417-
delete newProps.ref;
14181430
}
14191431
}
14201432

0 commit comments

Comments
 (0)