
When does React re-render? Explaining rerenders and how to optimize performance - fgerschau
https://felixgerschau.com/react-rerender-components
======
stupidcar
"React is known for providing a fast user experience by only updating the
parts of the UI that have changed."

This is wrong. Naive React code is rarely fast in relative terms. This is
because the whole design of React is _not_ based on updating on the parts of
the UI that have changed, but on re-rendering _everything_.

The whole concept of React, if you go back to the original "rethinking best
practices" intro, was that Facebook wanted to bring the server-side practice
of re-rendering the whole page for any change onto the client. This had the
obvious advantage that, combined with normalised state flowing downward
through the component tree, you would never end up with stale bits of UI, as
everything would all always be kept up to date.

However, tearing down and re-creating the whole DOM for every single render
was prohibitively expensive, so the virtual DOM was born. But, and this seems
to get lost in a lot of discussions of React, VDOM wasn't some magical leap
forward in UI rendering performance. It was a performance technique to make
React's paradigm possible. Possible, but not inherently fast, because there is
still a cost to re-rendering every component for every event. Any React
developer who's had to add React.memo to half their components, and add
visualisation to lists of just a few hundred items should know this.

Having written several substantial apps with both React and some of its
competitor frameworks, I'm always amazed to see React-only developers continue
to labor under the delusion than it is the fastest framework out there. It is
not. Its paradigm is a beautiful one to develop with in most ways, but there
is a significant price to pay in terms of performance.

~~~
davedx
I agree with most of what you say, but your conclusion:

> Its paradigm is a beautiful one to develop with in most ways, but there is a
> significant price to pay in terms of performance.

I've worked with React on-and-off since around 2013. I can count on one hand
the number of times that out-of-the-box idiomatic React code has incurred a
_significant price_ in terms of performance. The majority of my React apps
just work, they just render fast enough by default. One time I've had to
optimize it was when I had a React app that displayed a page of items from the
database without any pagination: once it got to a few hundred items, it would
indeed render very slowly. My optimization here was actually _not_ to start
memoizing or implement componentShouldUpdate -- it was to implement pagination
and search in the app, because the UX was more important than rendering speed,
and solving the UX solved the render speed.

I don't understand what kind of apps people are building with React that incur
these significant rendering performance costs? I could understand it if you're
building a realtime cryptocurrency exchange dashboard but I don't think most
people are doing that?

~~~
simion314
IMO pagination is garbage UX, any sane GUI tookit can render more then 100
items on screen. Imagine you text editor or spreeadsheet viewer would have a
limit of 100 rows. Sane GUIs don't create all this rows widgets from the start
but use smarter techniques. From what I seen this technique is called now
"windowing" but I knew it before by the name of "render item recycling"

~~~
danShumway
> any sane GUI tookit can render more then 100 items on screen

Sure, but it's uncommon that I _want_ more than 100 items on a screen in a
webapp. Infinite scroll is not an antipattern, but it is something that people
should be a little bit cautious of.

Users like context and clear anchors -- I like to be able to hit a
back/forward button and know where I'll end up. I also like pages to load
quickly and not make a ton of background requests on mobile connections, so if
I can have an initial page load under a few megabytes at max, and can wait for
the user to request data before loading it -- that's sometimes an attractive
proposition.

Multiple search engines used to experiment with infinite scrolling and virtual
rows, and as far as I know, most of the big ones went back to pagination, and
I think it's because sometimes pagination is genuinely better UX, particularly
when you're scrolling through a long list of blog posts or search results. Of
course you _can_ put thousands of elements in the DOM, and you can implement
virtual scrolling if you need tens of thousands or millions. But the DOM tree
is ultimately meant to be a human-traversable interface, and a <ul> element
with several thousand <li>s beneath it is arguably not very traversable.

There are no hard rules here and there are a lot of caveats to what I'm about
to say -- but I now bias towards avoiding DOM trees that are so big that I
couldn't stick the current tree in a text editor and mostly consume its
content textually, and that includes on dynamic web apps. The current state
I'm displaying to the user should be small enough that the user can wrap their
head around it.

The fact that React is so slow that you basically _need_ to at least
occasionally think about how you're rendering state is a separate
conversation.

~~~
deckard1
> it's uncommon that I want more than 100 items

I believe the discussion is _GUI_ items, not what you might consider data
items. Look at this current Hacker News page. Each comment, depending on how
fine-grained you're counting, looks to have about 5-6 or more GUI components.
In your browser viewport you may see a dozen comments. GUI components add up
quickly. These are also only the components _in view_. React, however, is
rendering the entire page. Easily hundreds of React components.

~~~
danShumway
But depending on how they're styled and what animations you're using, React
can easily handle rendering out 1000 DOM nodes.

To be clear, I don't want to act like it's not a real concern, I've been in
situations where the number of DOM elements I was rendering out with React
ended up being a performance concern. It's a real thing. Heck, I've been in
situations where React's insistence on computing the virtual DOM was a
performance problem. One of the downsides of component-based architecture is
that it's very easy to get into situations where you're completely pointlessly
duplicating effort parsing data multiple times and passing it around between
components and mapping it to new lists of data. It's a major weakness for
systems like React that encourage you to loosely couple all of your
components, even components that are inherently very related to each other.

But I think people are underestimating what that component/DOM limit is, even
for browsers like IE 11.

If you have 100 items on screen, and it's causing the browser to slow down,
you'd better be doing something at least somewhat complicated with those
elements. You'd better be sticking some SVG charts in there or something.
Otherwise I'm going to (perhaps uncharitably) accuse you of overcomplicating
your DOM and inserting a bunch of nodes that don't need to exist.

If someone wants to try and implement HN in React, they shouldn't be using 5-6
separate React components for a single comment, that's... I don't know how to
parse that kind of architecture. I don't want to be dismissive about it, and
different people have different approaches to programming -- but I feel pretty
strongly that kind of granularity is over-engineering.

~~~
simion314
Not OC but from my experience because you have some nested DIVs and spans and
some other node elements for each data item when you first load your data you
get a big hang/freeze because the browsers are still slow at creating this
elements, this is a browser issue I think, because most of the time you want
to have a big list of similar items there should not be such a giant work of
creating and computing similar DOM elements but I assume because of how
complex css engine is you get this giant freeze.

~~~
danShumway
Eh... you really _shouldn 't_ get a big hang/freeze if you're just talking
about inserting 1000 nodes into the DOM. It's not going to be fast enough that
you can do it at 60fps, but that's not the point of the DOM -- 60fps insertion
is not something we care about.

I mean, yes, some of this is dependent on CSS. If you're doing complex
rendering, or doing something strange with your fonts. I'm not going to make
absolute claims about performance in every case, and different people have
different interactions and requirements they're dealing with. There's caveats.

However, if someone comes to me and says, "I put 1000 DOM nodes into the
browser and my page froze", I might not immediately make strong claims about
the application, but I am going to immediately start looking around the
codebase suspiciously to see if they're doing something strange or abnormal.

The current HN page you're on right now has ~2500 DOM nodes. Does it freeze
for you when you load the page?

People complain a lot about DOM insertion, but while DOM insertion does
sometimes cause problems, I've found it's more often that the problems
happened before the insertion. At the point where DOM insertion actually
becomes a problem, I suspect we're usually no longer talking about lists of
1000 simple table elements, or just putting 100 search results on a single
page.

~~~
simion314
I am not sure what I did wrong, maybe if I had set some min/max height/width
on soem elements it would have improved the performance, or maybe if i changed
some flex-box property so regular layout or maybe if I changed some css rule
on some parent somewhere it would have worked better.

I am sure I can make a list with just text and not css I can insert 1000 text
nodes in it, the issue is if you have something mroe complex like a grid with
thumbnails and titles, and a small description under, and some css effect for
hover, and a few buttons when you hover over those items etc. In a sane GUI
this 1000 widgets are not created at all, only the visible ones are created
and when you scroll the data under the Widget is swapped, this is great
because you don't have to focus on performance or use pagination and then
pretend pagination is better UX.

Just to clarify I agree that there should not be many DOM nodes in a page, the
browser or framework widget should be smart and transparently recycle the
existing DOM elements, I imagine there would be some new List and Grid widgets
that are more advanced but that could be used by everyone with his favorite
framework.

~~~
danShumway
> In a sane GUI this 1000 widgets are not created at all, only the visible
> ones are created

As a side note, this gets a little bit at an issue I feel pretty passionate
about, which is that HTML is not a language for laying out interfaces, it is a
user-facing interface itself. HTML isn't for programmers, it's for users.

The sane GUI you're talking about is essentially virtual rows, they're just
baked into many native toolkits. A native interface doesn't insert everything
because it doesn't have to, its interface is purely visual. If there are
accessibility controls, they happen through a separate interface. If there are
keyboard controls, they're based on the widget, not based purely on the
content.

This is not a universal opinion, you will find people who disagree with me on
this. But I think the web is fundamentally different than that. The web at
least attempts to force your text interface and visual interface to be the
same.

I'm not sure if it was to you or someone else that I mentioned that many apps
(even native ones) are really just interactive documents when you think about
their data separate from their styling. In my mind, the innovation of the web
is that it acknowledges that, and it forces your interface to be a pure XML-
like tree. And then you can put some styling on top of that with CSS if you
want to. There are a few exceptions to that, but for the most part, HTML
doesn't really let you hide a ton of information.

So imagine if you were building an app in GTK, and GTK said, "okay, first give
me a pure-text representation of your interface that I can pipe to a terminal.
And then I will allow you to position boxes on the screen, but _only_ from the
text that you told me I can pipe to the terminal."

That's a very different paradigm than how most other application frameworks
work, and I think that the DOM's insistence of not having a lot of opaque
data-bindings where possible makes a lot more sense when viewed through that
lens. That lens also helps explain why some devs (myself included) got very
annoyed about Google messing with that paradigm because they had their own
'cute' idea for web components.

The downside of this paradigm is that the tools you're talking about like
data-bound lists aren't natively built into the DOM. You end up needing to use
3rd-party Javascript libraries for them. But for the most part, due to
Javascript's popularity, those libraries are easy to find. Although due to
Javascript's popularity, sometimes they are of dubious quality. :)

~~~
simion314
I understand, HTML is a document but it has this disadvantages and IMO if we
want to use HTML everywhere we need some default Widgets for List and Table,
don't change anything about current HTML but similar on how we have a Video or
Canvas element we might want something for this uses cases and not force each
developer re-invent the wheel. If is not built=int he browser I would like
some sane JS implementation (framework independent and without any
dependencies on 100 left-pads),

This thing where each developer re=invents same thing over and over and each
implementation is broken in some way it bothers me, like the search input in
Youtube has a dropdown with some suggestion, that dropdown popup can get stuck
open until you reload the page, so even Google engineers are not capable of
implementing a 100% working simple Widget.

------
davedx
The React premature optimization train is leaving platform 1, all aboard for
how to save 30 CPU cycles in your super complicated single page web app!

Memoizing a leaf component probably isn't worth doing unless it has some
incredibly complex logic in it.

Just like memoizing a single reduce operation probably isn't worth it either.

The React community is the absolute worst offender for premature optimization.

Measure first, then optimize.

~~~
jsf01
So true. This reminds me of one of my favorite tidbits from another vdom
framework’s documentation, in which using the equivalent of
shouldComponentUpdate is generally considered to be an antipattern. Whereas in
React, it seems idiomatic to just memoize and throw in shouldComponentUpdate
wherever you can as much as you can.

[https://mithril.js.org/lifecycle-methods.html#avoid-
prematur...](https://mithril.js.org/lifecycle-methods.html#avoid-premature-
optimizations)

~~~
onion2k
From the mithril docs you linked to:

"You should only use onbeforeupdate to skip diffing as a last resort. Avoid
using it unless you have a noticeable performance issue."

From the React docs ([https://reactjs.org/docs/react-
component.html#shouldcomponen...](https://reactjs.org/docs/react-
component.html#shouldcomponentupdate)):

"Use shouldComponentUpdate() to let React know if a component’s output is not
affected by the current change in state or props. The default behavior is to
re-render on every state change, and in the vast majority of cases you should
rely on the default behavior. [...] This method only exists as a performance
optimization."

Both framework developers are telling users the same thing.

~~~
jsf01
That’s a good point. There’s something in the difference between the two
communities then, where React’s tends to be encouraging of memoized components
and shouldComponentUpdate while mithril’s tends to advise against those
methods. The blog post behind this thread is a prime example.

~~~
dragonwriter
> There’s something in the difference between the two communities then, where
> React’s tends to be encouraging of memoized components and
> shouldComponentUpdate

I haven't seen that.

> The blog post behind this thread is a prime example.

But it's...not at all an example of what you describe. The blog post explains
how things work and how to optimize, it doesn't encourage optimizing without
demonstrated need, and the "even better" optimization approach it suggests
isn't using memoization or shouldComponentUpdate, but instead paying attention
to the basic component structure.

------
dham
This is just crazy React devs have to think about this. No other framework
makes you do this. Vue, Angular, Ember. The computer should be doing this for
us. It knows the properties we define, where we access them. Let the computer
do what it does best. It's generally not a huge issue with React DOM but in
React Native the performance issues will the kill the app. Luckily we have
Mobx which solves these issues.

~~~
ng12
You really don't have to think about it. This all falls into the category of
performance hacking. React renders are cheap, DOM renders are expensive.
Extraneous React renders are only a potential issue if you're doing something
ugly in your components.

------
sysbin
Question: Would vanilla js be faster than using react and when just using
functions that handle hiding & showing divs by changing the css visibility
value? I always assumed optimized react was somehow significantly reducing the
browser rendering in the background but I’m unsure now when reading the
comments.

~~~
aylmao
> Would vanilla js be faster than using react and when just using functions
> that handle hiding & showing divs by changing the css visibility value?

Yes.

> I always assumed optimized react was somehow significantly reducing the
> browser rendering

Let's say you add a <div>. The least amount of rendering you can do, is the
render associated with of that new <div>. If you code this manually, you can
guarantee you only add the div, and not modify anything else. This is
theoretically the best it can be, and so the best React can do as well, from
the rendering standpoint.

From the work standpoint, React does many more things associated with stuff
that make it work. From the vdom, to the scheduler— it's all overhead React
pulls in so it can function. So in holistically in terms performance, adding
that <div> yourself is always going to be faster than doing so through React—
the less code to run the faster something is.

What React gives you is a framework to think about your application's
architecture and execution. Adding a <div>, easy to do manually, no problem.
Rendering a whole application, with thousands of possible states, moving
parts, some data-fetching here, some animation there, all the while the user
is clicking buttons in the middle of state updates— that's harder. At that
point can you be sure you're hand-writing the optimal solution, taking into
consideration not only _what_ you're rendering, but _when_ it's updating,
_how_ the data is changing, and _where_ these mutations are taking place?
That's what React gives you. And maybe it doesn't (always) do what's optimal
for you, but it does at least provide order to all this complexity, so you can
more easily figure it out.

------
sktrdie
An interesting video by the creator of MobX going into detail about React vs
Mobx and specifically re-rendering, in a fascinating talk about "mutable
observable state". Suggest anybody interested in the subject of "react re-
rendering" to watch it:
[https://www.youtube.com/watch?v=NBYbBbjZeX4](https://www.youtube.com/watch?v=NBYbBbjZeX4)

------
jzer0cool
> Rendering is a term that can be understood on different levels of
> abstraction. Depending on the context, it has a slightly different meaning.
> In any case, ultimately it describes the process of generating an image.

Could we clear up more the meaning of rendering and how it optimizes the
performance mentioned in the article? Suppose the DOM has not changed, is the
DOM repainted? How about in the case you type into a textbox which expands
this container, causing adjacent elements to cascade into a new position?

Is this something similar to how mobile devices do not have to render content
that is "not" displayed on the screen, as in the example of a large ListView
so the mobile device does not have to immediate render any content that is
expected to be off screen.

~~~
jzer0cool
To be more specific, when ReactDOM.render() is called from a component -- is
the argument that the entire screen does not have to be painted?

~~~
fgerschau
By that I mean that there's a difference between calling React's render
function and rendering something to the browser DOM (native renders).

React will try to reduce the number of renders to the real DOM as much as
possible, so calling a component's render functions doesn't always imply a re-
paint of the UI.

Expanding a text box wouldn't usually trigger a re-render because this is
being handled by the browser and doesn't affect the state of the application
(just like any CSS rules). Reordering elements on the other hand will trigger
a re-render because the actual HTML element's position has to change.

So yes, the argument is that the entire screen doesn't have to be repainted.
In the ideal case it only repaints the parts of the UI that need to change.

------
rsynnott
> React is known for providing a fast user experience

No.

