-
Notifications
You must be signed in to change notification settings - Fork 47.3k
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
Take children
off props
#4694
Comments
This seems odd to me, I agree that the way If you forget about JSX for a second then all that So to me it sounds like all this boils down to the API decision of allowing children to be provided through varargs has significant negative side-effects that we don't want affecting JSX users, so it should move to a helper, not that EDIT: So from a technical perspective I can't think of any reason to make |
|
@syranide It's not just createElement that's weird. Even for JSX users, they can specify children as a prop on the jsx element (possibly via ellipsis) and simultaneously provide child nodes in the JSX element. You have the oddities for both jsx and non-jsx users alike. Changing the createElement signature doesn't solve the weirdness for jsx users. But that's not even the most powerful argument. The most powerful ones are both technical:
@jimbolla Yes, componentShouldUpdate is hard to implement if a component accepts children. This doesn't really solve that problem. But yes, I agree, processing |
It's not just Edit: just realized that's what @syranide said above. :-P |
Nit: they are not mutable, it's just that they're usually generated inside Babel hoisting will help this, but I'm not sure how well. Whereas the whole That's the problem I think @jimfb is describing, am I being correct? |
@gaearon Correct on all counts. The props of |
What does that even mean? Care to be more specific? :)
The only thing that makes this weird is because of XML and specifically the XML object model. The only thing that screws this rule up is that the DOM also allowed access to the serialized attributes through If anything, if we want to be consistent we would add I think that it is this mental connection to "attributes" that trips people up. However, it is also the most broken part of the DOM and something we've tried hard to get away from. So, no, from a "theoretical computer science perspective" the current model makes as much sense. Another thing that trip people up is that children are not "instantiated" before their parent. Meaning that you don't have access to the instances. The general misconception around children is related to that and not the nesting. E.g. people expected to be able to treat We might want to have that ability to pre-instantiate children at some point though. See for example: https://github.com/reactjs/react-future/blob/master/04%20-%20Layout/02%20-%20Layout%20Components.js#L23 I wanted to keep that open name for that potential purpose or something like it.
This is theoretically confusing if you're trying to do this. However, think about it, why would this happen in practice? What would the user do to end up in this situation? If you try to do this, while intentionally knowing that they're the same namespace, you might not know which priority or merging behavior applies, so you just avoid it. If you accidentally do this because you assume they're different namespaces, then you're probably not targetting HTML (since Besides, we could simply lint for this, or even disallow it in the transpiler.
That's incorrect. The This is the issue that @jordwalke has pointed out in the past. However, it is unclear how often this would actually be beneficial and how large the impact would be since you have wrapper objects around things anyway. Your maximum saving would be n / 2 - 1 for the rare case that it is actually possible to hoist. So it's not like you would be able to hoist the whole tree anyway. Being able to hoist is rare because of things like We have the same issue with Besides, if we end up using value types for props, they'll likely just merge onto the same allocation as the outer
Iteration and reflective APIs are expensive and does not JIT very well. So the solution is to try to avoid that as much as possible anyway. See for example: For known props on HTML elements I always wanted to create a function that reads all the props instead of iterating. E.g. function update(oldProps, newProps) {
if (oldProps.foo !== newProps.foo) {
element.setAttribute('foo', newProps.foo);
}
if (oldProps.bar !== newProps.bar) {
element.setAttribute('bar', newProps.bar);
}
// ...
}
That doesn't solve the For these reasons, we've rejected this proposal in the past and there's no new knowledge here so I'll close it out. Maybe if we get some actual numbers on perf impact in real apps we could reconsider if we don't think that alternative compilation strategies (e.g. value types) are likely to be realized soon enough to warrant a massive codemod/upgrade path. There is, however, another reason we might want to do this. We might change this as a compromise / concession if we try to standardize the ReactElement data structure as part of JSX. We don't really care about preserving XML's idiosyncracies but it is likely that other people using JSX to target XML might want to do that. If we ever get to a point where we can join others in standardizing JSX's data structure, then they might want to special case children and events. However, that is a big maybe and there are other quirks like |
It's odd that
The ReactElement will never be hostable, but the
Maybe we should, yes! Or stop treating style as a special case (object instead of string), but that's a different discussion. In practice, the style objects are oftentimes constant and thus already hoistable.
That solves the update props but not initial markup/render.
On a real component rendered by a real github user (chosen more or less at random), taking children off props and hosting the props object improved initial render time for my SSR renderer by more than 10%. It brought a 36ms render down to a 29ms render. Not a huge improvement, but very measurable, and the milliseconds add up. This was on a different JSVM (ie. not V8) so the relative costs could be a bit different and numbers should be taken with a grain of salt, but still, it's a valid data point.
The relative size of a codemod is debatable, depending on how far we would want to take such a change. It only NEEDS to affect the representation of inline elements and the OUTPUT representation of |
For a known whitelist it still works, and for the
If we change the inline representation, or output of
Can you point us to it? Is this a complex component or one that gets rendered a lot of times? Also note that hoisting isn't free. We might need to lazily initialize the elements since otherwise we can negatively impact the load time of the modules, or come up with another optimization. That's probably something can overcome though. |
You're still iterating to generate the markup, unless I'm failing to understand/see something (which is entirely possible).
Well, it's a dirty hack, but we could mutate the props before passing them to the component and then mutate them back when we're done, maintaining the illusion that ReactElements are immutable for the lifespan of the element.
It's the component discussed here: #2608 I've found it to be a particularly good one to benchmark against. I got my SSR render time down to 29ms, but without a couple of features (like |
children
on props is not only a bit awkward/random as per prior conversations with @jordwalke (ie. why is the prop "children" special cased?). Not a deciding factor, but certainly a bit irksume from a theoretical computer science perspective.element.children
feels cleaner thanelement.props.children
.React.createElement('div', {children: ...}, ...);
, since children is now specified with two potentially conflicting values. Imagine that the third argument isnull
or[]
or just a different value. I don't actually know what would happen off the top of my head, especially in the null case (is the null treated like undefined or does the third argument override the second?)children
on props means that the props object saved in the ReactElement can't be hoisted up by an intelligent runtime, which would otherwise be possible for runtimes/transpilers that can see that the non-child props don't depend on that particular render. This could save thousands of allocations per render.This is mostly an RFC issue, it's something to consider before doing inline elements. cc @sebmarkbage
The text was updated successfully, but these errors were encountered: