You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Unfortunately we still haven't figured out what the final semantics for refs. The current semantics relies on getting the current React owner. Therefore, we cannot apply this optimization if the ref attribute might be a string.
render(){// Neither of these...return<divref="str"/>;// ...are safe to inline...return<divref={possibleStringValue}/>;// ...because they might contain a ref.return<div{...objectThatMightContainARef}/>;}
Non-JSX
This can work on React.createElement or functions created by React.createFactory if the first argument is an inline object literal. Otherwise it is not safe since the object might be reused and mutated.
Only in Production Mode
This optimization should only be applied in production mode. Currently React.createElement fires various warnings for propTypes and key warnings if the __DEV__ flag is set to true or "production" !== process.env.NODE_ENV. This optimization would skip the warnings which would be very very bad in development mode.
The difficult part of this is figuring out a way that this will work in everyone's environment because not everyone has the ability to use different transpilers for development and production mode.
One solution might be to use a ternary and rely on minifiers to strip out the extra code:
Another solution would be to have different flags in the transpilers themselves but we'd have to make sure that people actually use them correctly. They will otherwise have problems due to not firing warnings, or think that React is slow because they screwed up their config.
The difficult part of this is figuring out a way that this will work in everyone's environment because not everyone has the ability to use different transpilers for development and production mode.
What do you mean by using different transpilers? You'd just be using the same transpiler flick a switch unless I'm misunderstanding something?
Another solution would be to have different flags in the transpilers themselves but we'd have to make sure that people actually use them correctly. They will otherwise have problems due to not firing warnings, or think that React is slow because they screwed up their config.
Developers already know to use a different build of React for development and production so I don't think it'll be confusing to introduce the same concept to the transpilation step. I've been toying with the idea of having a production and development mode in Babel as there are things like TDZ that I really want to enable by default but would absolutely kill performance in hot code and their only purpose is to basically prevent development errors.
Nit: developer education is a technical issue. :) Look at how much technical trouble we go through to add warnings and locking down the API so that developers don't unknowingly shoot themselves in the foot.
Yeah, absolutely agree. Probably should have been more specific, I was referring specifically to educating developers about any potential "production" mode in transpilers since there's no real way to warn them if they've compiled their production code in development mode.
Unfortunately we still haven't figured out what the final semantics for refs. The current semantics relies on getting the current React owner. Therefore, we cannot apply this optimization if the ref attribute might be a string.
There were mentions in the past of "inverting" refs, you instead provide a ref-object to ref which is updated. That sounded great to me, it gets rid of the implicit owner and possibly even the magic object... additionally you could set up a refs-object which could take care of lists of components, this does not work well today with strings.
But... what if we implemented ref as a callback(s) instead which receives the instance as an argument and something to distinguish whether it was mounted or unmounted, kind of similar to how onChange/valueLink works in a sense. That way we leave exact implementations up to the user and there is no preference for either individual instances or lists of instances nor where you store them (if you even store them). These could even be chained easily. But this is no longer a system for refs, it's basically externally chainable life-cycle method listeners which can be used to reimplement refs.
I imagine this could have other benefits in the future; expose all life-cycle callbacks and you could use it to say easily measure frontend objects without ever having to store the instance, nor provide complex logic to determine whether or not the frontend object actually updated. This exact use-case is just an idea, but this approach seems a lot more React:y, reusable and naturally efficient than refs which seems like something you should avoid unless explicitly dealing with uncontrolled components in which case you have no other option.
Perhaps I'm way off on the utility here, but it seems to me that it solves a lot of problems that refs as meant to solve but in better ways and actually storing refs should only necessary when dealing with uncontrolled components. All other uses should reasonable be capable of doing its thing directly on instance when the callback is called.
Re-implementing refs as-is could look something like the code below, it could easily be made into a reusable helper. It could take any number of shapes, perhaps a key isn't even interesting and just putting all instances in a Set is enough. You decide.
To keep updated measurements of a component you would simply as below, if the editor didn't update then you automatically avoid re-measuring for free. If only the editor updates, but not your own component, you still remeasure and get the updated height.
@syranide We already implemented both first-class refs and then switched them to callbacks in #2917. You haven't been keeping up. :)
It is not very convenient and very imperative since you need to keep your own reference to it. The timing of life-cycles isn't ideal too. Therefore I'm not comfortable deprecating the current refs until we're sure which model is final, and make sure that we have a good upgrade path (codemodable).
Performance question: is it preferable to always include the key: null, ref: null or is it ok to omit those if they are undefined? eg. maybe browsers can better optimize the code if the shape of objects is always the same.
@elsassph keeping those properties allows JIT compilers (such as V8) to match the "hidden class" of the objects. Generally speaking this should give better performance.
@trueadm You'll benefit from that anyway, there will just be more "hidden classes". I suspect there's nothing to be gained in this context, the performance difference is small and spread over the whole project it's unlikely to even be measurable and if you overdo it you're actually increasing the cost as a result of dealing with larger objects and more processing involved in creating them (again probably immeasurably). So I would say it's largely counter-productive, if you're rendering large lists it may be a good idea to keep the signature the same (so the hidden class is kept in fast cache), but even then I doubt it's really measurable.
@syranide The performance can definitely make an impact. As you point out, it's very good for large lists of virtual nodes, where you need to aggressively diff them. This is one of the many reasons why Inferno gets very good performance, especially when it comes to complex node tress that rapidly update.
Keeping object shapes monomorphic can give significant benefits in regards to V8 and how it decides what to deopt (I've spent countless hours going through the ASM output using IRHydra2 checking for such cases).
The performance can definitely make an impact. As you point out, it's very good for large lists of virtual nodes...
Generally there is a single code path being repeated and it's a very unusual setup if you're mixing keys with no keys and refs with no refs in a list.
Keeping object shapes monomorphic can give significant benefits...
Yes, but preemptively adding key: null, ref: null is unlikely to do that. It only matters if you're creating elements that otherwise have the exact same signature. This is very uncommon for React elements in my experience, use of ref is frowned upon and rarely used, use of key is largely related to the intent of the component. If you were to print out all the unique React element signatures before and after doing this for all of them I'm quite confident the difference would be very small and the actual impact immeasurable. There's nothing wrong with reducing hidden classes, but preemptively adding null key/ref seems entirely counter-productive as a general rule.
@syranide It looks great on benchmarks if done right, but in the grand scheme of things, it's unlikely to make much difference as the app code the user has created is most likely to be the bottleneck in any case, but it's worth having if you can get it for free at a compiler level.
Activity
sebmarkbage commentedon Feb 22, 2015
cc @sebmck
sebmck commentedon Feb 22, 2015
What do you mean by using different transpilers? You'd just be using the same transpiler flick a switch unless I'm misunderstanding something?
Developers already know to use a different build of React for development and production so I don't think it'll be confusing to introduce the same concept to the transpilation step. I've been toying with the idea of having a production and development mode in Babel as there are things like TDZ that I really want to enable by default but would absolutely kill performance in hot code and their only purpose is to basically prevent development errors.
sebmarkbage commentedon Feb 22, 2015
Yea, that's what I meant.
Developers already have minifiers that they only use in production mode. This is effectively the same thing.
sebmck commentedon Feb 22, 2015
Ah, right. Yeah, definently more a developer education issue than a technical one.
sebmarkbage commentedon Feb 22, 2015
Nit: developer education is a technical issue. :) Look at how much technical trouble we go through to add warnings and locking down the API so that developers don't unknowingly shoot themselves in the foot.
sebmck commentedon Feb 22, 2015
Yeah, absolutely agree. Probably should have been more specific, I was referring specifically to educating developers about any potential "production" mode in transpilers since there's no real way to warn them if they've compiled their production code in development mode.
syranide commentedon Feb 22, 2015
@sebmarkbage
There were mentions in the past of "inverting" refs, you instead provide a ref-object to
refwhich is updated. That sounded great to me, it gets rid of the implicit owner and possibly even the magic object... additionally you could set up a refs-object which could take care of lists of components, this does not work well today with strings.But... what if we implemented ref as a callback(s) instead which receives the instance as an argument and something to distinguish whether it was mounted or unmounted, kind of similar to how
onChange/valueLinkworks in a sense. That way we leave exact implementations up to the user and there is no preference for either individual instances or lists of instances nor where you store them (if you even store them). These could even be chained easily. But this is no longer a system for refs, it's basically externally chainable life-cycle method listeners which can be used to reimplement refs.I imagine this could have other benefits in the future; expose all life-cycle callbacks and you could use it to say easily measure frontend objects without ever having to store the instance, nor provide complex logic to determine whether or not the frontend object actually updated. This exact use-case is just an idea, but this approach seems a lot more React:y, reusable and naturally efficient than
refswhich seems like something you should avoid unless explicitly dealing with uncontrolled components in which case you have no other option.Perhaps I'm way off on the utility here, but it seems to me that it solves a lot of problems that refs as meant to solve but in better ways and actually storing refs should only necessary when dealing with uncontrolled components. All other uses should reasonable be capable of doing its thing directly on instance when the callback is called.
Re-implementing refs as-is could look something like the code below, it could easily be made into a reusable helper. It could take any number of shapes, perhaps a key isn't even interesting and just putting all instances in a Set is enough. You decide.
To keep updated measurements of a component you would simply as below, if the editor didn't update then you automatically avoid re-measuring for free. If only the editor updates, but not your own component, you still remeasure and get the updated height.
sebmarkbage commentedon Feb 22, 2015
@syranide We already implemented both first-class refs and then switched them to callbacks in #2917. You haven't been keeping up. :)
It is not very convenient and very imperative since you need to keep your own reference to it. The timing of life-cycles isn't ideal too. Therefore I'm not comfortable deprecating the current refs until we're sure which model is final, and make sure that we have a good upgrade path (codemodable).
syranide commentedon Feb 22, 2015
@sebmarkbage * facepalm * ;)
add optimisation.react.constantElements transformer - react/react#3228
sebmck commentedon Mar 29, 2015
I've done a complete and stable implementation of this (see attached commit). Also works perfectly with #3226 (no real reason for it not to).
koba04 commentedon Sep 3, 2015
@sebmarkbage
Do you have any updates on the issue?
sebmarkbage commentedon Sep 10, 2015
This proposal now need to be revised to include a
$$typeoffield as per #4832sophiebits commentedon Sep 10, 2015
@sebmarkbage shh I was working on a PR! now @sebmck will beat me for sure…
sophiebits commentedon Sep 10, 2015
(babel/babel#2352)
elsassph commentedon Apr 21, 2016
Performance question: is it preferable to always include the
key: null, ref: nullor is it ok to omit those if they are undefined? eg. maybe browsers can better optimize the code if the shape of objects is always the same.trueadm commentedon Apr 21, 2016
@elsassph keeping those properties allows JIT compilers (such as V8) to match the "hidden class" of the objects. Generally speaking this should give better performance.
syranide commentedon Apr 21, 2016
@trueadm You'll benefit from that anyway, there will just be more "hidden classes". I suspect there's nothing to be gained in this context, the performance difference is small and spread over the whole project it's unlikely to even be measurable and if you overdo it you're actually increasing the cost as a result of dealing with larger objects and more processing involved in creating them (again probably immeasurably). So I would say it's largely counter-productive, if you're rendering large lists it may be a good idea to keep the signature the same (so the hidden class is kept in fast cache), but even then I doubt it's really measurable.
trueadm commentedon Apr 21, 2016
@syranide The performance can definitely make an impact. As you point out, it's very good for large lists of virtual nodes, where you need to aggressively diff them. This is one of the many reasons why Inferno gets very good performance, especially when it comes to complex node tress that rapidly update.
Keeping object shapes monomorphic can give significant benefits in regards to V8 and how it decides what to
deopt(I've spent countless hours going through the ASM output using IRHydra2 checking for such cases).syranide commentedon Apr 21, 2016
Generally there is a single code path being repeated and it's a very unusual setup if you're mixing keys with no keys and refs with no refs in a list.
Yes, but preemptively adding
key: null, ref: nullis unlikely to do that. It only matters if you're creating elements that otherwise have the exact same signature. This is very uncommon for React elements in my experience, use of ref is frowned upon and rarely used, use of key is largely related to the intent of the component. If you were to print out all the unique React element signatures before and after doing this for all of them I'm quite confident the difference would be very small and the actual impact immeasurable. There's nothing wrong with reducing hidden classes, but preemptively adding null key/ref seems entirely counter-productive as a general rule.trueadm commentedon Apr 21, 2016
@syranide It looks great on benchmarks if done right, but in the grand scheme of things, it's unlikely to make much difference as the app code the user has created is most likely to be the bottleneck in any case, but it's worth having if you can get it for free at a compiler level.
elsassph commentedon Apr 21, 2016
Thanks that's the kind of considerations I'm facing - making objects smaller by not including null properties VS shadow classes potential benefits.
sophiebits commentedon Apr 22, 2016
On the React team we've been operating under the assumption that it's better to include the nulls.
I'm going to close this since the babel plugin now exists.
chore: update `README`
add @babel/plugin-transform-react-inline-elements