12 ms·
When does React re-render? Explaining rerenders and how to optimize performance
- stupidcar 6y ago"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.
- jkhaui 6y agoCan you please explain what you mean by every component re-rendering? Maybe I'm just ignorant, but I don't think that's correct (unless you made a really bad app). Other than that I agree with the gist of your post
- capableweb 6y agoYeah, I also don't think that's correct, in most cases. React updates the entire tree internally when anything changes, but actually only changes DOM nodes who's prop or state has changed. The problem is, the check of props and state that React does by default is naive, in that it doesn't check if something is deeply equal, only shallow. So using numbers in props works correctly, but not array of numbers. So if you use anything other than booleans, numbers, strings and so on, where equality works as expected, only changed components will render to the DOM. But if you have arrays or objects, the checks React does will always trigger an update of the DOM, as `[] === []` or `[] == []` will never return true, even though they are the same. The way to work around this is to implement your own `shouldComponentUpdate(newProps, newState) <boolean>` and do your own equality checks. Maybe a side-effect of the poor equality we have in JS-land.
- quantummkv 6y ago> Yeah, I also don't think that's correct, in most cases. React updates the entire tree internally when anything changes, but actually only changes DOM nodes who's prop or state has changed. That internal tree update is the root cause of all the problems. During that update, React runs all the javascript in the component, and that of all of it's child components, which then run their child components till the entire tree is done. Sure, only a small part of the actual DOM will change. But the sheer wastage that happens to update that small part is criminal.
- sfvisser 6y ago> Yeah, I also don't think that's correct, in most cases. Actually, stupidcar assessment is completely correct. > React updates the entire tree internally when anything changes, but actually only changes DOM nodes who's prop or state has changed. Sorry, but you're mixing up a lot of concepts here! When you tell react(-dom) to render, it recursively calls all the render() methods of your entire component hierarchy and updates the entire virtual DOM, but only mutates the actual browser's DOM for the parts that are changed. Yes, there are ways to block a render based on prop staleness using shouldComponentUpdate/PureComponent/React.memo. These are explicit optimisations on top of the default render-everything model. Props and State play into this story quite a bit differently. When a component-local state changes react only renders that specific component (with everything below it). Props are passed down on every render and are by default not compared for staleness so don't prevent re-renders. Props and DOM updates have nothing to do with each other: props are properties of components, not the DOM! Sure, the DOM can be directly or indirectly be influenced by those props. > Maybe a side-effect of the poor equality we have in JS-land. Yes, this is true. A lot of reacts's quirks are a result of JS's poor notion of equality.
- baddox 6y agoMost web developers are probably not particularly concerned about how fast their code is in relative terms. There’s a huge amount of room for UI library benchmark competitions that all lie easily within “good enough for the user.” It’s also important to note that the virtual DOM isn’t just (or perhaps even primarily) about performance. It’s also about maintaining DOM state across updates, like focus, cursors in text inputs, etc. Modern browsers on modern devices are fast enough that for many web apps you probably actually could just document.write the entire DOM from scratch on every component state update if you didn’t care about that DOM state.
- jsf01 6y agoMost web developers should be more concerned with performance than they are. In the wild, on a fairly recent MacBook Pro, I frequently run into sites where I deal with 100ms+ input lag. And every time I check, it’s always React (sometimes Angular) under the hood. These are sites with great dev teams too. Stripe, Airbnb, Squarespace to name a few, suffer from severe performance problems on modern hardware. And those issues stem more from the diffing and reconciling stages than the actual DOM itself, whose updates are minimal for things like entering text into an input.
- onion2k 6y agoAnd every time I check, it’s always React (sometimes Angular) under the hood. What is it when the app is fast? It's probably React or Angular. Those are just the most popular frameworks, so when you look you'll probably see one of them. In good hands they can make fast apps. In poor hands they can make slow apps. Those frameworks aren't the problem.
- ng12 6y ago> And those issues stem more from the diffing and reconciling stages than the actual DOM itself I don't see how you could know that without reading the code.
- jsf01 6y agoBecause the only update occurring is the changing of text in an input. Yet due to how React works it’s diffing a much larger tree.
- lowwave 6y agoReact's shouldComponentUpdate hook seems to be a bit of after thought and a bit of relatively complex over engineering. A comparison of mithril can see that React is almost 2 times as slow. https://mithril.js.org/framework-comparison.html https://mithril.js.org/framework-comparison.html The only reason to use React for SPA is the job market, and that companies like the fact that is backed by Facebook.
- jsf01 6y agoReact is far greater than 2x as slow on some metrics, like boot time, but the newer versions actually aren’t as bad as the pre-fiber versions that those benchmarks are based on for DOM updates. For boot up time React is still about 5x worse than Mithril, but on create & update benchmarks they’re now at least in the same ballpark. https://krausest.github.io/js-framework-benchmark/current.html https://krausest.github.io/js-framework-benchmark/current.ht...
- azangru 6y agoNit: shouldComponentUpdate is a lifecycle method, not a hook. The corresponding approach in React's new world is React.memo.
- jbverschoor 6y agoThank you! Amen and all that stuff :-)
- deleted 6y ago[deleted]
- davedx 6y agoI 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?
- azangru 6y agoI saw in a recent tweet (can't find now) that React-based web apps were draining mobile phone batteries significantly faster than the comparison, because, with the constant re-renders, they were generating a heavier CPU load. This is something I had never thought about before.
- davedx 6y agoIt makes sense, if it's changing very quickly and re-rendering the whole component tree -- say it's some kind of realtime stream of updates or a graphql subscription. And then of course you should try and optimize so only that component is re-rendered. My main argument is against people who try to argue that you should memoize a form input field or a div or something that changes once every few seconds or so. It's just pointless.
- runawaybottle 6y agoThat app could be just as battery draining as a vue or angular app. We’d have to know the details.
- azangru 6y ago> This is wrong. Naive React code is rarely fast in relative terms. Do you remember Ryan Florence's talk Hype at ReactConf 2015, where he compared React performance to that of Ember and Angular 1 [0]? Back in 2015, in relative terms, React seemed fast! [0] https://www.youtube.com/watch?v=z5e7kWSHWTg https://www.youtube.com/watch?v=z5e7kWSHWTg
- vimslayer 6y agoThis talk gained some controversy because the comparison with AngularJS wasn't very fair, IIRC they weren't using some basic optimizations like $index with ngRepeat, which would've been the best practice. React requires the key property for similar purposes and actually displays a console error if you forget it, unlike AngularJS for $index.
- Vinnl 6y agoExactly. The virtual DOM is wonderful because it allows you to model your components as (roughly) a function that transforms some state into a description of the view, without having to keep track of how consecutive changes impact the result. High performance is not the goal, though acceptable performance in the general case is a requirement.
- runawaybottle 6y agoBut to say rendering performance of the vdom with it’s ability to sync dom updates into one one reflow/repaint was not one of the selling points of React (one of the first notable vdom proponents) is neglecting history. When you also couple with the fact that at the time Angular 1 was doing things like dirty checking, render performance with the vdom and the ability to sync render updates was definitely a selling point for me. The apparent motivation behind React seems to have morphed a bit.
- wtetzner 6y agoI feel like the marketing behind React's speed was similar to that of Clojure's. Neither one is particularly fast, though they tend to be fast enough. But I think they included good performance in their marketing to avoid arguments against using them "because they're slow".
- Vinnl 6y agoThat is certainly true; compared to Angular 1's shenanigans, it was far harder to accidentally make React slow, so of course word quickly got around that it was faster than Angular.js (something interpreted as just "fast"). However, I'm not sure if I'd call that the motivation behind it, or even conscious marketing from React's side, rather than just an artefacts of its times.
- qw 6y agoVDOM itself is not a silver bullet either. One example is Imba (https://www.imba.io/ https://www.imba.io/) which compiles to JS and is supposed to be much faster. The "todo list" example they give is almost 50x faster than React. They use a technique they call "memoized DOM"
- learc83 6y agoSvelte is another component based js framework that specifically avoids using a VDOM.
- deleted 6y ago[deleted]
- sktrdie 6y agoI agree with this post but can you clarify what you mean by "you would never end up with stale bits of UI"? What do you mean by "stale bits"? An example of the problem would be useful... and also why the "rerender everything" is a solution to such problem. For instance I already see that something like Mobx that has observable state doesn't need to re-render everything and still gives you guarantees about not having stale values (thanks to the way observable works with it).
- davedx 6y agoThe 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.
- fgerschau 6y agoWhy write inefficient code when it's just as easy to do it right? I also wrote an article about this, if you can write efficient code without sacrificing readability I think it's worth the (small) effort. https://felixgerschau.com/premature-optimization-javascript https://felixgerschau.com/premature-optimization-javascript
- Etheryte 6y agoNothing you've brought up in the article is actually optimization though? "Use const" and "reuse code" aren't optimizations, they're simply programming. The submitted article is fairly similar in some sense. You address optimization but skip out on the most important step of optimizing - measuring what you need to optimize. Tacking memo, ref and key onto everything isn't free and if those parts of your application weren't the problem you're actually making it slower.
- MaxBarraclough 6y agoIt makes the claim that using const can improve performance. It doesn't properly substantiate this claim though, and it doesn't seem likely that it's true. It makes the same claim about code reuse, with the reasoning that code is more likely to be JIT compiled if it's a single hotspot rather than two, well, 'warm spots'. Again it doesn't properly back up this reasoning.
- londons_explore 6y ago> The React community is the absolute worst offender for premature optimization. They are also the worst for "no optimization it takes 5 seconds to load" pages.
- sysbin 6y agoQuestion: 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 6y ago> 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.
- rimliu 6y agoDOM operations are costly whether you do them or React does them. What React offers is avoiding unnecessary DOM operations. However if you need to perform two operations, and React still needs to perform two operations, vanilla will be faster by avoiding all the extra work with VirtualDOM rerendering and diffing. Virtual DOM is taken by many as granted and cost-free, which is not true. If your SPA is comples and hats a lot of components, then on each data change all thad virtual rerendering and diffing kicks off and even if at the end no DOM opreations need to be done, i.e. nothing changes visually all this adds up in terms of CPU resources.
- jzer0cool 6y ago> 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 6y agoTo 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 6y agoBy 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.
- The_rationalist 6y agoRendering is such a misleading term.. React recompute the dom mostly does not imply a repainting / layout recalc / recompositing / display list cache invalidation, it has mostly nothing to do with graphical rendering despite being what I believe people understand by this word.
- sktrdie 6y agoAn 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
- dham 6y agoThis 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.
- sktrdie 6y agoExactly what I thought! Mobx creator did a talk recently on re-rendering: https://www.youtube.com/watch?v=NBYbBbjZeX4 https://www.youtube.com/watch?v=NBYbBbjZeX4 Makes me wonder why React doesn't work in the same way... using observable state.
- ng12 6y agoYou 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.
- rsynnott 6y ago> React is known for providing a fast user experience No.