"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.
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?
I work on an app that edits and renders fairly complex indoor floorplans to SVG, all using React.
We deal with large hospitals and airports, so it's normal for us to have tens of thousands of components mounted at once - pagination isn't a viable solution for our purposes.
We've had to tap basically every optimization that React apps can do when managing large numbers of elements - however this has been worth it for us:
- We can use the same code to render interactive plans on the client, and to render to print-quality PDFs on the server
- We can use the same framework in our UI and plan rendering
The downside is that it's more CPU intensive than alternatives like a WebGL renderer, but for now that's a tradeoff worth making.
> more CPU intensive than alternatives like a WebGL renderer, but for now that's a tradeoff worth making
Mixing traditional React with WebGL is another option. There's react-three-fiber[1], and custom/simpler hybrids are also possible. Though I've not tried server-side rendering.
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"
> 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.
Not all lists have thumbnail with images, it could be a taqble with pure text, I think that doing a single request and grabbing 1000 rows of text and have it instantly accessible is much better then paginating it and doing the Next Page and loading screen etc.
I am not talking about infinite data here, but data that fits in one single request, that also fits in RAM but does not fit all at once as rendered DOM elements, so the fix is that the GUI widget for this table is smart and instead of rendering 1000 tables will not do that, will render just the visible part and the a few rows above and under the viewport, when you scroll the data in the table cels is changed and no new DOM elements are created. This should be soemthing that the developer should not think about, in this sane GUI toolkits you just do something like
widget.dataProvider =myData;
Edit:
Also for the case of youtube videos, you don't load "download" the thumbnails, those are just string, a sane toolkit will not load all those thumbnails until you bring them into view. I hate YT implementation, you get 3 rows of video then you scroll wait 2 seconds for next 3 rows to load, scroll again, wait again 2 seconds. You could have loaded the strings for all the videos, then lazy load the thumbnails when needed and skip the step of fetch me the next page of string after the strings arrive now fetch the images for those strings.
> 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.
Having ~100-200 items, I'd damn love to have them all on the screen. It seems like the best option out of the trio of render all, pagination or infinite scroll. Pagination is faster than infinite scroll, infinite scroll is more convenient than pagination, but both make it hard to reliably display data in sequence (as the whole data set isn't loaded immediately anymore) - and more importantly, both break search. However bad and bare-bones searching is in the browser, it's still better than search reimplementations in web apps, because the latter aren't any more sophisticated, and also have limited scope.
That's just my opinion, and a point of annoyance with modern SPAs. Most of the time when I have to scroll a lot, or traverse a bunch of pages, all I really want is to be able to CTRL+F for the thing I'm looking for without changing the state of the application.
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.
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.
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.
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.
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.
> 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. :)
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.
Sometimes also called a virtualized list (react-virtualized is a popular implementation). Perfectly fine if keeping a lot items in memory isn't a problem (it rarely is). Of course fetching a lot of data from a backend can be a problem.
>IMO pagination is garbage UX, any sane GUI tookit can render more then 100 items on screen.
Well, I guess that's the web in 2020 in a nutshell, lol. What would you do with a list of 100,000 items? Just slowly scroll them out, one by one while your scroll gets faster and faster? At one point, are you just a billion pixels down? What does "scrolling up" even mean down there? Or do we just limit all lists to 1000 items, period?
It depends on the application, should we limit text editors to 1000 lines?
Should you limit the files in a folder to some number because the ListWidget has a limit of 1000 ?
No, the solution is too use our brain and realize that you can open giant log files in a text editor , you can have a list with many items in, this problem were solved already the issue is that HTML was designed for documents not applications and the built-in lists or tables are not optimized at all, where a List widget or DataGrid Widget in other toolkits were optimized.
I am not expecting a developer to put some hard work to fix this in his app, browsers or frameworks should do this, I dislike the idea that pagination is good UX as an excuse for "pagination is the cheap solution"
I 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.
It 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.
>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?
I think it's just easy to make mistakes (bad loops, overly trigger-happy interpretations of "state change", etc), especially for all things "lists" (which make up half the web). Since people make mistakes, I'd always consider easy-to-make mistakes a design flaw, in this case affecting performance.
Also there's a psychological cost to making incremental "loading" operations visible. Although some mysterious "research" is frequently cited that people just fucking LOVE spinning loading icons and gray placeholder boxes that are never where the actual content will pop in, it's just instantly making my perception of a site slower. If a website just takes 5 seconds to load, okay. If, during that time, it just pops in and out 50 components, 3 different loading bars, spinning whirlies and blurry placeholder images (where the one I actually want to see always loads last), I'm suddenly having a "delay experience". It's like a whole firework of bullshit happening to remind you the site HAS NOT loaded yet.
Can 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
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. 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.
> 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.
> 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.
> 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.
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.
If 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.
As you said yourself, only children components will be re-rendered. This is relative to the component that had it's state changed (using setState or the useState hook). So not the whole tree rerenders, only the affected component and it's children. And again, when we speak as "re-render", the render() functions of the components are called and then the VDOM diffing happens. That is, even if child components got -rerendered (in react terms), there might not be a DOM update at all (which is the slow, costly operation).
> That is, even if child components got -rerendered (in react terms), there might not be a DOM update at all (which is the slow, costly operation).
Crawling a large React tree's render stack is also a costly operation. Not as much as DOM manipulation but it is still costly, and React is created in such a way that it requires you to include everything in that stack, even if it never changes. Which is one reason we have Portals.
(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?
Yes, of course, I realized how the html re-render part works just now reading your reply, which I had known before but forgotten. sort of embarrassing.
> 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!
This 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.
VDOM itself is not a silver bullet either. One example is Imba (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"
Exactly. 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.
But 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.
That 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.
I 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".
Most 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.
Most 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.
And 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.
I agree with that, but I highly doubt that many web sites with performance problems can attribute those problems to React or whatever UI library they’re using. The reason that you see React or Angular when you check under the hood is that those are extremely popular UI libraries.
Are you sure there isn’t a slow server request happening before the UI update? I would be very surprised that you’re regularly encountering web sites with noticeably slow React reconciliation. It’s difficult to make that slow.
> Most web developers are probably not particularly concerned about how fast their code is in relative terms.
Absolutely. As long as our app performs without any input lag on IE11 I'm happy. Our users don't care and our higher-ups don't care either. They care that we built out new features quickly and have a better user experience than our competitors. If React lets us trade some minor rendering performance for better developer productivity it's doing it's job.
I 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).
React 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.
This sounds like good advice, but from experience I've found that optimizing code by adding things like memoization after you've discovered it's too slow in the context of an app is a lot harder than than writing something that's efficient to start with, especially if a feature is mature and other parts of the code are resistant to change. There's a balance to strike between spending time writing optimal code and actually shipping, but if there are low-hanging fruit available and you choose not to take advantage of them you'll probably regret it.
This whole "don't do premature optimization" is one the worst creeds in software development, right below "just hack together a temporary MVP for now, we'll write a good one later"
As someone who spent weeks trying to optimize a react native app, believe me, this is not going to go the way you think it is. It never will. Not without rewriting half of your app and earning the ire of your customers for being a slow hog along the way.
> Memoizing a leaf component probably isn't worth doing
It isn't when you only have 10 components. By the time you have a thousand of them in a big app, you will have a thousand small performance paper cuts in you application. And they will bleed your app while you can do nothing but watch.
Not exactly a thousand. That was a bit of a hyperbole.
But I have used hundreds of components in some apps. Most of them were big enterprise apps that had a lot of input fields for data entry. Given the was data entry works in React, especially with the general form practices, it was a nightmare when I first started and was a follower of the entire premature optimization gospel.
For a more similar and public example, just have a look at how sluggish the jira webapp feels.
Nothing 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.
It 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.
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.
"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.
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.
> 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.
> Whereas in React, it seems idiomatic to just memoize and throw in shouldComponentUpdate wherever you can as much as you can.
Absolutely not, wrapping everything in shouldComponentUpdates probably leads to worse performance as the comparison checks are more expensive in hot paths than just rendering everything again (same with PureComponents and so on).
Premature optimisation is a problem, but I'd say in general it's a better approach to just start out by doing the fast thing. Memoising (or using PureComponent or whatever) is an absolutely minimal cost if you're using an appropriate architecture – my experience suggests that it's way easier to do that from the outset with everything—adding some exceptions where required—rather than trying to detangle a large SPA that uses mutable props everywhere later on.
It's probably good advice to avoid lots of custom shouldComponentUpdate logic though. That's smelly.
The whole silly meme of "premature optimization is the root of all evil" is a 100% useless statement. Of course, you shouldn't do it yet if it's premature- that's the definition of premature.
But if it's almost the same amount of effort to write faster code from the get go- especially if you KNOW the code will be speed-sensitive, then you should absolutely try to do the fast thing over the slow thing. That seems obvious...
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.
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.
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.
> 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.
If the vdom can do the diff and sync all the changes to one DOM update, you only trigger one re-layout. That’s one of the main reasons to have the vdom, to offset shitty DOM performance by using app memory. The browser really sucked/sucks.
I urge everyone to keep track of the history of JS frameworks, and why we even gravitate towards these frameworks.
DOM 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.
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
> 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.
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.
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.