(I work on React.) This isn't quite right. If we recommended that PureComponent be used everywhere, it would probably be the default already. Rather -- the comparison to decide whether or not a component should be rerendered costs something, and in the case that you do want to rerender, all of the time spent checking whether you should have rerendered is wasted.
Instead, we'd suggest you be conscious of where you need to do the comparisons. It's usually only in a couple of places in your app. Good candidates are on the children of a long list or around large parts of the app that change independently (that is, cases where you know the parent should often rerender but the child shouldn't). A few well-placed shouldComponentUpdate (or PureComponent) uses can go a long way.
When using immutable data structures the comparison in `shouldComponentUpdate` is very cheap. Assuming that, using pure components everywhere is very tempting for simplicity.
Correct me if I'm wrong, but React creates an object for every component to pass props around. Constructing that object is linear in the number of props. An additional `shouldComponentUpdate` check for each component is also linear in the number of props. So using pure components everywhere is at worst adjusting the constant.
Also, in a world where PureComponent is the default, perhaps React could monitor the "hit rate" of `shouldComponentUpdate` and decide to not call it if a component returns true too often?
PureComponent always uses === on each prop value (regardless of type), so the comparison cost with PureComponent should be relatively flat regardless of whether you're using (persistent) immutable data structures. There's also a cost in looping over the prop values and indexing into the (megamorphic) hash map for each props object that is harder to quantify.
> React creates an object for every component to pass props around. Constructing that object is linear in the number of props. An additional `shouldComponentUpdate` check for each component is also linear in the number of props. So using pure components everywhere is at worst adjusting the constant.
That's true. We've just been a little wary of ending up in a "death by a thousand cuts" scenario where every single unnecessary PureComponent makes your app a little bit slower. A number of teams at Facebook have also chosen to use PureComponent for most use cases so you're not alone.
> Also, in a world where PureComponent is the default, perhaps React could monitor the "hit rate" of `shouldComponentUpdate` and decide to not call it if a component returns true too often?
That would be cool, doing a sort of "profile-guided optimization" for React trees. I don't think this would be feasible to give improvements at a per-user/per-session level but could potentially give significant wins if you had a way to collect stats across many different users.
The equality check is always the same in pure components. Whether the data is immutable or not, pure components do a reference equality check. This will result in failing to update correctly with mutable data.
For simplicity, I guess you could go with PureComponent by default, and remove it as an optimization instead, which is what I've been doing with good results.
As dmnd pointed out, I'm not entirely convinced that dropping PureComponent everywhere is not a good idea.
For our app specifically, we unfortunately do data loading in a lot of places, which means that there are quite a bit more than just few places that can benefit from being a PureComponent. Our current idea is to try to make everything a PureComponent, and go back and 'unpurify' the places that have wasteful sCU comparisons. This might not turn out to be a good idea though, for the reasons you mentioned.
I'll make sure to do some profiling in our codebase once we get to that point, and see how much wasted sCU we are doing.
e.g.,
const MyComponent = props => (...);
vs class MyComponent extends PureComponent {...}The idea is that this is used to suggest whether when rendering a component, the children are new components, or simply existing components with updated props.
E.g. You have a component that renders a list of items. If you add an item, what you would rather do is create a single new child component and re-use the previously generated children vs. discarding the child components from a the previous render and creating a brand new set.
If you create a key based on a hash of the props this will decrease the time taken to re-render an set of child components with identical props, but at the cost of discarding an existing component and creating a new one if one of the props changes.
Or something like that.....
See for a bit more detail: https://facebook.github.io/react/docs/reconciliation.html
I'll assume your key is quick to calculate. If a prop change causes the hash / key to change then react doesn't know how to reuse the dom fragment. I guess it will just reuse the stuff that's in the same location in that case so maybe it's fine. You'll lose the advantage that keys give you in the first case which is dealing with reordering.
And actually, just because the key changes or not, that doesn't cause a rerender or not. The rerender is always going to happen. Right? Unless I'm missing something.
> Instead, we'd suggest you be conscious of where you need to do the comparisons.
I'm not following, wouldn't that mean PureComponent by default and opt-in specialized checks when needed?
His point is that you don't always need that check - that check has overhead too!
Basically PureComponent is just a specialized case of shouldComponentUpdate so it's better practice to be mindful of where you're calling that anyhow.
It's also why there's the push to use stateless functions (when you can) because the kind of pseudo micro-optimization that PureComponent actually is can have counter-intuitive consequences.
render() {
const views = this .props.dataList.map((d, i) => {
return <Data data={d} index={i} onDelete={this.handleDelete} />
});
}
handleDelete(index) {
//...
}
I guess they just call that function from within the Data component with the correct parameter. But then, Data component needs to know how to call that function. Is there a better way?[1] https://facebook.github.io/react/docs/handling-events.html
[2] https://babeljs.io/docs/plugins/transform-class-properties/
One is to bind the callback function to a certain index in advance and then supply that callback to your child via props so it already knows the relevant index and doesn't need calling with any extra parameters. The trouble with this one is that it creates a new function every time, which is inefficient in itself and horrible if it winds up causing your child to rerender because props changed even though in practice you're passing an identical function every time.
The other is to supply a callback where the child component is responsible for supplying the extra index information when it calls the function, either by directly passing the index as a parameter or via an indirect route such as using data-* attributes as 'kentor mentioned. This is more efficient, but it creates tighter coupling between the child and parent components.
In practice, it seems most projects adopt the second strategy because the tighter coupling is usually much less of a problem in practice than the performance penalty, but neither solution is ideal. Unfortunately, JavaScript's semantics don't make it easy to have the equivalent of a "deep equal" comparison on functions that would return true for two copies of the same function with the same bound values.
Yep
> Is there a better way?
We're experimenting with some other options internally. Once we have a better sense of what works best, we will post another article on our learnings :)
return ( <Data value={d} index={i} deleteDataAt={deleteDataAt} /> );
Then your onClick in the child would be: onClick = (_e) => this.props.deleteDataAt(this.props.index);
Correct me if I'm missing anything!
> Fixing the issue is pretty simple*. We simply need to short circuit the re-rendering for a subtree if we know that the subtree hasn’t changed.
Not a frontend guy but I've seen this theme more than once on HN recently. It seems to me that addressing this anti-pattern would be built right in to modern React components. Isn't efficient DOM manipulation by pruning non-necessary changes kind of their thing?
Since these can be slow (although much faster than a DOM change), it makes sense to check and skip those steps when you can.
The focus of most of this article is how you can skip those steps when you're components input data is unchanged and common gotchas related to that.
Some other great posts in this vein are: https://facebook.github.io/react/docs/optimizing-performance... and http://benchling.engineering/performance-engineering-with-re...
But that sentence is talking about VirtualDOM rendering - which still takes time - not DOM manipulation.
Basically it's saying "if we don't need to do work, we shouldn't". Which React already does for DOM manipulation and this kind of optimization does it for virtualDOM creation.
This is one place where using Clojurescript wrappers of React has a real payoff - immutable datastructures make equality checking very very cheap.
Edit: sorry guys, didn't knew you were all on HN :(
Even if I agree that premature optimization is the root of all evils, I like when the language and the framework I use make it writing fast code as easy as writing slow code.
I wonder if anybody has evaluated, the improvement they get in development speed using React when they add the time they have to spend to optimizing their React code.
In my experience, I am not even frustrated with React itself most of the time but with the other things in the ecosystem.
We have survived over 3 years with a massive application (over 2000 modules) and tons of wasteful re-rendering, but have only now started to notice any sluggishness. That makes React pretty fast in my mind.
Having said all that, we are experimenting with an 'all PureComponents' approach to potentially make React, but whether that turns out to be a good idea is yet to be seen (object comparisons in sCU are not free).
If the total re-render is occurring under 16.67ms (60fps) in a non-trivial use-case I'm usually fine leaving it as-is. Most render cycles taking longer than that involve operations on large data sets or events firing at the millisecond level (mouseover, drag, etc) - and those are worth fixing as they'll actually have noticeable impacts on the user experience. But I tend not to optimize just to pat myself on the back for "optimization".
I am more concerned that the code that runs during a re-render is not rebuilding huge static lists that never change and so. I see that over and over again in React apps. I tend to build a lot of the static stuff in the ComponentWillMount stage to prevent that. Only when already optimised code is getting to slow I start thinking about preventing re-rendering if possible.
My reason for not starting with ShouldComponentUpdate is that you have to be very careful not creating nasty bugs with that like I did some times. If a year later you write some code and it doesn't work for some reason it is painful if you find out after a long search that a stupid child component did not update for some prevention rule you forgot about. I really try hard to avoid premature optimisations.
Also, recalculating derived data (building static lists) in render() is very wasteful, and is also another gotcha when using PureComponents (object copying). Removing object copying from render() speeds up the actual render() call and allows you to take full advantage of PureComponents.
We are experimenting with ways to make all of this a bit easier, and hopefully will have some good news to share in Part 2 :)
We've just started using mobx ourselves. There's still a weird contention between events and state that I'm yet develop patterns for but overall I'm really happy with it.
React is a powerful way to build your front-end, but it's power is in structure / code.
In my experience with React, fine-tuning components for faster rendering is always a pain. Too much magic happens behind the scenes and too difficult to keep that in mind while writing your beautiful components.
There isn't an inherent one, it's just that writing for both is much harder than just for one.
If anyone's interested, my React/Redux links list has a large section of other articles on various React-related performance concerns, including benchmarking and optimization approaches: https://github.com/markerikson/react-redux-links/blob/master... .
Another technique that I find very useful is to use a dedicated data declaration component which then decides if children should update or not. Try grid sorting on this page: https://worldoscope.cxjs.io/yyrsmjk
[1]: https://cxjs.io