
Optimizing React Rendering - dounan
https://flexport.engineering/optimizing-react-rendering-part-1-9634469dca02
======
sophiebits
> So all you have to do is use PureComponent everywhere and you’re good to go.
> There’s nothing more to it. Enjoy your new blazing fast React app!

(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.

~~~
chatmasta
Is it considered best practice to use the "key" of a child element to signify
if it should rerender? This is what I've been doing and it seems to work well.
If a parent updates its state, and then as a result, changes the props of some
or all of its children, the parent sets the "key" of a child to effectively a
hash of the props of that child.

~~~
evan_
keep in mind that changing the key will completely destroy and create a new
element- a more expensive operation if all that's necessary is changing the
class or something

~~~
dounan
nit: new component instance (not element)

~~~
evan_
I think we're both right- I believe it will create a new element and a new DOM
element, rather than mutating the existing ones.

~~~
dounan
You're right. I misinterpreted your first comment to refer be referring to
React elements, which is quite different from DOM elements
[https://facebook.github.io/react/blog/2015/12/18/react-
compo...](https://facebook.github.io/react/blog/2015/12/18/react-components-
elements-and-instances.html#elements-describe-the-tree)

------
mercer
This is actually a pretty decent article, in part because it signals its
audience properly: (semi-?)experienced React developers who haven't dealt with
optimization yet.

~~~
bobsam
so basically the Facebook app team?

Edit: sorry guys, didn't knew you were all on HN :(

~~~
devopsproject
The downvotes are sad. Are people this far gone that they can't take a little
criticism?

~~~
e1g
Offtopic, but regarding the downvotes -

I have been using FB front-end projects for several years, and today I see
them as _the_ gold standard for a supportive, mature, well-governed, and
consistent FoSS ecosystem. I struggle to recall any snarkiness, cynicism, or
even rudeness. I'm sure it happens, but I closely monitor several of their
projects and the behavior is mostly exemplary even without considering the
size of the user-base.

Besides near-exemplary FoSS behavior, FB engineers must solve problems well
beyond what most industry will ever attempt, and then present some of their
solutions on a silver platter for the rest of us. Of course, it's not perfect,
and sometimes confusing, and sometimes over-engineering, but I would
personally pay $xx,000 per year to use any library of the same standard as
they give out for free.

So when I see a callous comment that takes nothing to write and could cause
someone to feel degraded about their vast contributions, I downvote. It's not
about safe places, it's about professional respect and courtesy.

~~~
bobsam
Seriously? HN has really short memory...

[https://news.ycombinator.com/item?id=11057857](https://news.ycombinator.com/item?id=11057857)

[https://news.ycombinator.com/item?id=10066338](https://news.ycombinator.com/item?id=10066338)

[https://news.ycombinator.com/item?id=14082491](https://news.ycombinator.com/item?id=14082491)

------
uranian
For some reason the focus is always on preventing re-rendering if it comes to
optimising a React app. I understand that it can be really effective and is
often the easy way out, but re-rendering in itself is not costly and I don't
really care about it the most of the time.

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.

~~~
dounan
That's a great point, and being aware of the dangers of shouldComponentUpdate
is what this post is all about. There are a lot of gotchas when using
PureComponents, and can lead to bugs if you're not careful.

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 :)

------
acemarke
Pretty good post, minus the advice to always use PureComponent everywhere (per
Ben's comment in this thread).

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...](https://github.com/markerikson/react-redux-
links/blob/master/react-performance.md) .

------
metalliqaz
From the article:

> 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?

~~~
robto
I think it is an anti-pattern that is a result of the language it's built on.

This is one place where using Clojurescript wrappers of React has a real
payoff - immutable datastructures make equality checking very very cheap.

~~~
doctorcroc
A good patch for this is ImmutableJS

------
mendelk
Along the same lines as the advice in TFA: If you pass an object literal as
the `style` prop, it'll always re-render, because the object reference will
change. Solution: instead of the object literal, pass a reference to an
existing object.

------
ChrisCinelli
One thing that has been bothering me is that React is slow by default. It re-
renders everything every time unless you start looking into
shouldComponentUpdate.

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.

~~~
dounan
In our experience, React is not necessarily 'slow' by default. Re-constructing
parts of the element tree each time can be a bit wasteful, but it is only as
slow as your render() methods, and that is outside of React's control.

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).

------
joncampbelldev
From what I've found in my current job creating a js react app as well as side
project with cljs is that advanced optimsation of js react === basic usage of
clojurescript reagent. I've found no redeeming features when developing the is
app, issues with hot reloading and constant integration work to get everything
to play nice with immutable js

~~~
joncampbelldev
*the js app

------
positivecomment
Is there special logic in react that binds arguments of a function to the
props with the same name? I'm talking about the handleDelete "fix" in the
article:

    
    
        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?

~~~
nip
By using the property initializer syntax [1] [2]

[1] [https://facebook.github.io/react/docs/handling-
events.html](https://facebook.github.io/react/docs/handling-events.html)

[2] [https://babeljs.io/docs/plugins/transform-class-
properties/](https://babeljs.io/docs/plugins/transform-class-properties/)

~~~
Silhouette
That solves the problem of binding 'this', but doesn't solve the problem of
supplying the index to the callback function. Essentially you have only two
options for doing that.

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.

~~~
azundo
Another is to wrap the first option in _.memoize so you only create functions
once per argument and you don't get unnecessary rerenders. Just remember that
_.memoize only uses the first arg for cache lookups.

------
mstijak
I'm the author of CxJS[1] which uses React for rendering. Before rendering,
the whole tree is scanned for data changes and with that information available
shouldComponentUpdate is very simple.

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](https://worldoscope.cxjs.io/yyrsmjk)

[1]: [https://cxjs.io](https://cxjs.io)

------
wbc
Would using mobx to manage state solve most of these problems?

~~~
aidos
Yeah. That's exactly what I was thinking as I read through the list of
gotchas.

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.

------
drinchev
I would generally say that there is a reciprocal relationship of performance
and maintainable code, no matter what you use.

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.

~~~
pvg
_there is a reciprocal relationship of performance and maintainable code_

There isn't an inherent one, it's just that writing for both is much harder
than just for one.

------
CCing
Off-topic: what is the theme and color syntax theme ?

~~~
dounan
Atom editor with the 'One Dark' theme

------
pawelwentpawel
Quick question - can printWasted be used with RN too?

~~~
dounan
Haven't tried this myself, but this might be what you're looking for:
[https://github.com/facebook/react-
native/issues/9146#issueco...](https://github.com/facebook/react-
native/issues/9146#issuecomment-277434523)

~~~
pawelwentpawel
Thanks! I'll give it a go.

