-
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
Change ReactChildrenMutationWarningHook to Object.freeze children #7455
Conversation
@@ -194,12 +186,28 @@ ReactElement.createElement = function(type, config, children) { | |||
// the newly allocated props object. | |||
var childrenLength = arguments.length - 2; | |||
if (childrenLength === 1) { | |||
props.children = children; | |||
if (Array.isArray(children)) { | |||
let childArray = children.slice(0); |
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.
I think it's important that the behavior stays the same across different envs so I call slice
here regardless of the environment (prod vs. dev). Without this we won't be able to catch cases like var children = [...]; <Comp children={children}>
since we don't want to freeze the original array reference from userland.
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.
cc @sebmarkbage performance?
Is this incremental improvement? If so, can I merge #7410 until this gets reviewed? |
} else if (childrenLength > 1) { | ||
var childArray = Array(childrenLength); | ||
let childArray = Array(childrenLength); |
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.
This can be const, right? The reference identity remains the same.
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.
Oh yeah, good catch!
At a high level, this looks fine to me. I flagged the slice as a performance note for Sebastian. It would suck if we regress the prod build performance because we were trying to improve dev build performance. If Sebastian signs off on the extra allocation/copy, I have no objections to this diff ( 👍 ). |
So we can't merge #7410? |
I already merged #7410 in the meantime but this might be a better solution. |
|
||
class Wrapper extends React.Component { | ||
componentDidMount() { | ||
setTimeout(() => { |
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.
Why is setTimeout
here necessary?
Rebased and updated.
|
Friendly ping 😀: I just rebased and updated this PR. Do we want to take this approach to fix #7424? To sum up, it calls |
Can you please help me understand how to read this profiler script output? |
if (childrenLength === 1) { | ||
props.children = children; | ||
if (Array.isArray(children)) { | ||
childArray = children.slice(0); |
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.
Does this mean that if we pass them through multiple levels of components, the same array could get slice(0)
d many times?
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.
Yes, that's unfortunate. :( Do you think there's a better way to do this?
However, it should be more performant than the current approach, which slices in the ReactElement()
function (https://github.com/facebook/react/blob/master/src/isomorphic/classic/element/ReactElement.js#L138) for every component that has more than one child. The new one slices in createElement
and only when an user explicitly passes an array as children
. If this makes sense then I guess the question is whether we want to slice 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.
I’m mostly worried about prod here.
I don’t think we can afford to do more allocations in prod.
Yeah, sorry, |
What does the benchmark measure? |
No, it only measures time. The percentages ( |
I don’t think allocating more in production is the way to go here. this.props.children.<mutatingMethod>() or (var|const|let) <name> = (this.)?props.children;
<name>.{mutatingMethod}() If this covers most cases and gets included into the default set of |
It probably suffices to freeze the array if ReactElement creates it and do nothing otherwise, don't you think? |
I’m not sure when people would try to |
Could you rebase pls? |
b310f7f
to
51752fb
Compare
- only freeze children array created by createElement
51752fb
to
470c945
Compare
Sure, just rebased. |
Thanks! |
FYI: There's no way this is a patch level change - it changes what was a warning to start throwing. Sure, it's dev-only but that's not ok to take in a patch release (we avoid introducing new warnings in patch releases, we definitely can't start throwing). Let's be a bit more conservative with the semver assignment so we don't sneak breakages in at the wrong time. |
@zpao does that mean that there will be |
I'll be more conservative, thanks for correcting! |
- only freeze children array created by createElement (cherry picked from commit 38c4ade)
Alternative of #7410 that fixes #7406. This should also fix #7424 and #7426 (the false-positive warning).
Saving and comparing the
shadowChildren
is pretty expensive -- we canObject.freeze
the JSX children instead.Benchmark:
Reviewers:
@jimfb @vjeux