10 ms·
"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
by 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.
- deckard1 6y ago> A lot of reacts's quirks are a result of JS's poor notion of equality. God, yes. And I'm amazed everyday to run into senior engineers that do not understand how equality works in JS. People are also easily confused if they happen to use Redux, because Redux also adds a layer of memoization for you and will not re-render if none of the state (props) or dispatch functions change.
- bryanrasmussen 6y agoI believe the argument is as follows: If you have a React Tree that outputs the HTML that is your full page some of your components in that tree will not be re-rendered because the virtual DOM tells you you do not need to re-render them (where we us re-render to mean the React render function is called for that particular part of the tree) but the last render of that component is still saved somewhere and when your whole tree is ready to re-render the HTML of your page (where re-render means actually sending changes to the DOM and causing all the page to re-render) I don't know enough about how React internals work but I suppose if you have a parent that re-renders but a child does not (React render called again), both will still render (HTML render) at the time of updating the DOM, as I cannot see a logical way that it could not be so.
- miracle2k 6y agoIf I understand you correctly, you have it backwards. If a component renders in React, that is, the render function is called, then ALL children's render() function are also called, unless you do specific optimizations such as React.memo. So the whole tree is rerendered. When it comes time to apply those changes to the DOM, that is, generating HTML on the page, wherever your render() functions returned the same result as before, the DOM is not touched.
- uryga 6y ago(i hope i'm not spreading misinfo, i'm not super experienced with React) it may be that way with regular Components, but afaik Pure components are smarter - the idea of Pure components is that react only rerenders the components when the props change. (or something in a hook, if you're using those). though i guess a Pure component is basically equivalent to what you'd get with memo?
- miracle2k 6y agoYes, PureComponent, React.memo and shouldComponentUpdate are all the same mechanism.
- littlecranky67 6y ago
- 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).