-
Notifications
You must be signed in to change notification settings - Fork 46.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Do not clone key and ref getter props #6268
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,19 @@ var RESERVED_PROPS = { | |
|
||
var specialPropKeyWarningShown, specialPropRefWarningShown; | ||
|
||
function isValidConfigRefOrKey(config, name) { | ||
if (__DEV__) { | ||
return config.hasOwnProperty(name) && | ||
!Object.getOwnPropertyDescriptor(config, name).get; | ||
} | ||
|
||
return config[name] !== undefined; | ||
} | ||
|
||
function getConfigKey(config) { | ||
return '' + config.key; | ||
} | ||
|
||
/** | ||
* Factory method to create a new React element. This no longer adheres to | ||
* the class pattern, so do not use new to call it. Also, no instanceof check | ||
|
@@ -133,14 +146,16 @@ ReactElement.createElement = function(type, config, children) { | |
'React.createElement(...): Expected props argument to be a plain object. ' + | ||
'Properties defined in its prototype chain will be ignored.' | ||
); | ||
ref = !config.hasOwnProperty('ref') || | ||
Object.getOwnPropertyDescriptor(config, 'ref').get ? null : config.ref; | ||
key = !config.hasOwnProperty('key') || | ||
Object.getOwnPropertyDescriptor(config, 'key').get ? null : '' + config.key; | ||
} else { | ||
ref = config.ref === undefined ? null : config.ref; | ||
key = config.key === undefined ? null : '' + config.key; | ||
} | ||
|
||
if (isValidConfigRefOrKey(config, 'ref')) { | ||
ref = config.ref; | ||
} | ||
|
||
if (isValidConfigRefOrKey(config, 'key')) { | ||
key = getConfigKey(config); | ||
} | ||
|
||
self = config.__self === undefined ? null : config.__self; | ||
source = config.__source === undefined ? null : config.__source; | ||
// Remaining properties are added to a new props object | ||
|
@@ -284,14 +299,17 @@ ReactElement.cloneElement = function(element, config, children) { | |
'Properties defined in its prototype chain will be ignored.' | ||
); | ||
} | ||
if (config.ref !== undefined) { | ||
|
||
if (isValidConfigRefOrKey(config, 'ref')) { | ||
// Silently steal the ref from the parent. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any specific reason why you're changing comparisons to be looser here? They used to be true for null but are now false. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Allowing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this approach should keep behavior consistent. Let me know what you think and I'll add a test for the function isValidConfigRefOrKey(config, name) {
if (__DEV__) {
return config.hasOwnProperty(name) &&
!Object.getOwnPropertyDescriptor(config, name).get;
}
return config[name] !== undefined;
}
function getConfigKey(config) {
return '' + config.key;
}
...
ReactElement.createElement = function(type, config, children) {
...
if (isValidConfigRefOrKey(config, 'ref')) {
ref = config.ref;
}
if (isValidConfigRefOrKey(config, 'key')) {
key = getConfigKey(config);
}
...
ReactElement.cloneElement = function(element, config, children) {
...
if (isValidConfigRefOrKey(config, 'ref')) {
// Silently steal the ref from the parent.
ref = config.ref;
owner = ReactCurrentOwner.current;
}
if (isValidConfigRefOrKey(config, 'key')) {
key = getConfigKey(config);
} |
||
ref = config.ref; | ||
owner = ReactCurrentOwner.current; | ||
} | ||
if (config.key !== undefined) { | ||
key = '' + config.key; | ||
|
||
if (isValidConfigRefOrKey(config, 'key')) { | ||
key = getConfigKey(config); | ||
} | ||
|
||
// Remaining properties override existing props | ||
var defaultProps; | ||
if (element.type && element.type.defaultProps) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this indirection (passing
'ref'
instead of directly readingconfig.ref
) cause any performance regressions in the production path? I’m not well versed in optimizations like this but it’s a super hot code path so we better make sure we don’t regress on it in prod.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're adding potentially 3 function calls. It could very easily be reduced to 2 since
getConfigKey
isn't very necessary. The function has the same undefined condition that existed previously, but it now skips assigning null again since the value is already null. That doesn't seem like enough to make any measurable difference in performance, but I'm sure if you have any thoughts on proving that empirically.