41

I understand that there are two arguments for React's Virtual DOM being faster -

  1. It updates ONLY those elements that actually need to be updated (using diff).

  2. It batches the updates and hence we update the real DOM only a single time. Thus the repainting is also done only once which otherwise would have been done multiple times.

I have questions regarding both these points -

  1. As far as I know, all the modern browsers are efficient enough to update only the required elements in the DOM. For example, if I have two 'p' tags and I change the text in one of the p tags using a button click, then only that p tag will be updated by safari (I have verified this using paint flashing). So how is point 1 an advantage if it is already being implemented by the browsers?

  2. How exactly does React batch the updates? Eventually React will also have to use the DOM api to update the real DOM. So why is that if we directly use the DOM api then the changes will not be batched whereas when React uses it then they are batched?

chetan
  • 916
  • 1
  • 8
  • 15
  • 1
    Does this help explain? https://reactjs.org/docs/reconciliation.html – Drew Reese Apr 16 '20 at 08:22
  • 2
    @DrewReese it doesn't address the points I have mentioned. It only explains the diff process. But it doesn't mention how that is an advantage. It also doesn't discuss batching. – chetan Apr 16 '20 at 08:24
  • 1
    The diffing is the advantage; updating a virtual DOM is faster because updating in memory is faster than updating on screen. @David explained it well. For #2, react is open source, look at the code to see how they batch the updates. – Drew Reese Apr 16 '20 at 08:35
  • 3
    @DrewReese I understand that updating in memory is faster. But eventually you have to update on the screen. That makes the update in memory only an overhead. The argument is that it helps in updating only those elements that are changed. I am saying that the browsers already do that. So what exactly did react do to improve the performance? – chetan Apr 16 '20 at 08:39
  • 1
    Yes, but not really. Browsers do repaint only what updated. React batches these updates so it isn't going back and forth for *every* little element that changes in the DOM. The cost is repainting, as David pointed out. The less often you go to repaint the more efficient the framework. – Drew Reese Apr 16 '20 at 08:44
  • @DrewReese So can we say that batching is the only advantage and point 1 is not actually an advantage as many people describe it? – chetan Apr 16 '20 at 08:48
  • @chetanraina no. even after optimizations were made to mitigate these issues after the popularity of React, React's approach of using a predictably optimizable UI tree (that is especially well suited for web app UI trees since its assumptions and design fit virtually all normal use cases) still provided performance advantages. This can be seen eg: back when Angular UI switched to a similar optimization approach to React, and still wasn't as performant. I can't remember the article, but they made a post explaining in detail way back in the day. – user120242 Apr 19 '20 at 05:47
  • It's the reason that immutability has been made such a big deal of. The use of cheap object comparisons, when you can assume that all objects are immutable, became an important concept. That it was more than just because it was easier to grok and debug, which was also important. Probably a bit of an oversimplifcation, but it's the gist of it. – user120242 Apr 19 '20 at 05:52
  • Also because of the things that browsers have to do, they could never take as "pure" an approach as what React does in created assumptions that allow optimizations where side effects can be assumed to not apply. It's why you still have `shouldComponentUpdate`, and there are still valid use cases where it is needed to mitigate performance issues. – user120242 Apr 19 '20 at 05:57

2 Answers2

40

I have found the answer to my question.

The key is to understand the purpose of the Virtual DOM.

First we have to see what approach React takes to render the components.

Different javascript frameworks take different approaches to detect changes in the data model and render them on the view.

Consider AngularJS. When we refer to our data in an Angular template, for example in an expression like {{foo.x}}, Angular not only renders that data but also creates a watcher for that particular value. Whenever anything happens in our app (click event, HTTP response, timeout), all the watchers are run. If the value in a watcher has changed then that value is re-rendered in the UI. By running all the watchers AngularJS is essentially finding out where it needs to make the changes. The process of running these watchers is called dirty checking.

React takes a different approach. Whenever there is a state change in a React component, instead of finding out where to make the changes (like AngularJS), React re-renders the entire UI from scratch (with the updated state).

But this approach of React has a problem. To re-render the entire UI means to re-render the entire DOM tree. This is a problem because DOM updation is a slow process (due to reflow and repainting).

This is where React's Virtual DOM comes in. A Virtual DOM is just a representation of the Real DOM in form of javascript objects. It is just a tree data structure of plain javascript objects that exists in the memory. As compared to the Real DOM, rendering of the Virtual DOM is much faster because it is never rendered on the screen (no reflow or repainting needs to be done).

So how does the Virtual DOM solve the problem? When we load our app, React creates a Virtual DOM that is an exact virtual copy of the Real DOM. Whenever there is a state change in a component, instead of re-rendering the entire Real DOM, React renders an entire new Virtual DOM (with the updated state). Then it does a diff between the old Virtual DOM (the initial copy of the Real DOM) and this new Virtual DOM (rendered after state change) to find out the changes between them and it does ONLY those changes in the Real DOM. In this way, the entire UI is re-rendered (by rendering an entire new Virtual DOM) but only the minimum required changes are done in the Real DOM.

So when it is said that "Using Virtual DOM React updates only those elements that need to be updated" (point 1 in my question), it means that with the help of Virtual DOM React is overcoming the limitations of its own approach (approach of rendering the entire UI from scratch).

This answer also explains the same concept.

I have seen some answers that state that DOM manipulation using React is faster than using the DOM api because the DOM api re-renders the entire DOM tree whereas React re-renders only those parts of the DOM tree that need to be changed. This is NOT true. All modern browsers are efficient enough to update only those parts of the DOM tree that need to be changed. This can be verified using paint flashing in developer tools of browsers (also see this answer and this answer). Even if we assume that the DOM api does re-render the entire DOM tree, still this reasoning is false because the internal code of React itself has to use the DOM api to update the DOM. If the DOM api did re-render the entire DOM tree then React would also re-render the entire DOM tree because eventually it also uses the DOM api to update the DOM.

 
As for the second point, React actually makes batching easier for us.

In React, while the reads are done on the Real DOM, the writes (state changes) are not done on the Real DOM. Instead the writes are queued. Then when all our reads and writes have been processed, a new Virtual DOM is built based on the writes. Then diffing is done between the old and new Virtual DOM and then React writes the required changes to the Real DOM to update it. Hence eventually all the writes on the Real DOM are done together in a single reflow.

But we can manually also, without React, write our code in such a way that first all reads are done and then all writes are done. React makes batching easier because with React we don't have to care about doing the reads and writes together and React will automatically batch the writes for us. So React does not make things fast. It makes things easier.

 
In conclusion we can say that React is not actually faster. It is easier. As Pete Hunt says in this video, "React is not magic. Just like you can drop into assembler with C and beat the C compiler, you can drop into raw DOM operations and DOM API calls and beat React if you wanted to. However, using C or Java or JavaScript is an order of magnitude performance improvement because you don't have to worry...about the specifics of the platform. With React you can build applications without even thinking about performance and the default state is fast.".

This post by Rich Harris also states that it is a myth that "the Virtual DOM is fast".

chetan
  • 916
  • 1
  • 8
  • 15
  • A simple concrete example is very long lists or tables with and without a `key` property. A browser can never optimize for this, because that is not within the domain of the browser's job. You will always need an abstraction on top to implement the optimization. – user120242 Apr 19 '20 at 06:07
  • @user120242 can you explain this key optimization in detail? – chetan Apr 19 '20 at 06:14
  • https://reactjs.org/docs/lists-and-keys.html#keys Basically if you have a list of children ,
      [
    • , ... ....really long list of children]
    If you were to insert somewhere in between it would not be able to update just the one or two elements. If you tried to re-render it yourself every time with a really large list, you'd probably end up having the browser freeze up while updating.
    – user120242 Apr 19 '20 at 06:18
  • Actually now that I'm looking at it that's not be a good example. If you were writing this code yourself, you'd probably already optimized for only affecting what has been updated. This is react specific. – user120242 Apr 19 '20 at 06:20
  • Okay. If I update the list using DOM api then many elements will be updated. But eventually react will also have to update the list using the same api. So what prevents those elements to be updated in this case? – chetan Apr 19 '20 at 06:22
  • Ya, you could update it yourself. The point is not to need to reach into the DOM until you have to. You could, update it all as one big innerHTML, or keep a list in memory that you transform into updates. But then you'd be reproducing a simplified version of what React does. And you run the risk of depending on "viewing" the DOM to make sure your updates are in the correct place. React's approach in this way emphasizes making sure you've done the proper transformations in memory on the virtual DOM, before ever going to update. It's still valid to claim that it is part of the optimization – user120242 Apr 19 '20 at 06:26
  • "React's approach in this way emphasizes making sure you've done the proper transformations in memory on the virtual DOM, before ever going to update." Why don't do those proper transformations directly on the Real DOM? – chetan Apr 19 '20 at 06:32
  • I see what you are saying and it holds some truth to it, but I think it's an oversimplification. For example, yes, Google has highly performant apps whose UI framework doesn't depend on the concept of a virtual DOM. It's essentially taking a different approach to the optimizations React _encourages_ with its design. – user120242 Apr 19 '20 at 06:37
  • Basically React can be seen as a framework for giant "innerHTML" building but as components, where updates can be mostly assumed to be optimized as long as you follow its framework. The basis of all this being the virtual DOM's predictably optimizable structure that applies well to basic UI. Note: browsers used this concept themselves after React was introduced and the performance gap was significantly reduced, but there are things that browsers cannot really address, nor can they expose the structure they use for this optimized rendering. – user120242 Apr 19 '20 at 06:45
  • 3
    I think the point that my question was trying to make (that you acknowledge has some truth to it) is what Pete Hunt says in the video - "React is not magic. You can always write assembler code to beat C. In a similar way you can always use raw DOM operations and DOM apis to beat React" (paraphrased). But the purpose of React was never to beat Vanilla JS. The purpose of React was to transfer the job of maintaining state changes from the developers to a library. – chetan Apr 19 '20 at 06:52
  • 2
    Browsers don't really "batch" changes though, just like in these frameworks, the DOM is still just a plain js object for the browser too. What it does is to delay the *reflow* until the next paint (which happens at monitor refresh rate). Only when the js will require the computed values will it force a reflow synchronously. see https://stackoverflow.com/questions/47342730/javascript-are-dom-redraw-methods-synchronous/47343090#47343090 – Kaiido Apr 19 '20 at 07:46
  • @Kaiido You are right. If have replaced 'batching' with 'delay'. – chetan Apr 19 '20 at 09:03
  • Right, but different DOM operations have different performance characteristics. And to the benefit of React which has already used its predictably optimizable VDOM to optimize operations, it can make decisions on how to do operations in the best way possible. For example deciding between using cloneNode or createElement or innerHTML. Again, you could probably do the same with handwritten code, but that's like saying I can write pure ASM that outperforms a C# compiler. You'd probably do not much better than if you used it or worse, and it'd be painful (or fun.. I guess). – user120242 Apr 19 '20 at 15:07
  • @user120242 That is precisely my point, that React makes things easier. But stating that React's DOM manipulation is faster than the DOM manipulation done using DOM api calls is just not true. One can always write pure javascript code to beat React. – chetan Apr 19 '20 at 15:21
  • @Kaiido can you mention a source that says DOM changes are not batched? I wasn't able to find any. But i did find this [post](https://www.phpied.com/rendering-repaint-reflowrelayout-restyle/) that says "The browser will setup a queue of the changes your scripts require and perform them in batches". – chetan Apr 24 '20 at 22:40
  • @Kaiido can you also mention a source that says DOM is a plain js object because this [answer](https://stackoverflow.com/a/21593385/7730507) states otherwise. – chetan Apr 24 '20 at 22:56
  • @chetanraina that post says exactly what I said: they will wait to trigger the reflow and repaint. But the changes in the DOM tree are done synchronously. The other answer only states that browsers aren't written in js. And when I spoke about the DOM, that was a shortcut for "the manipulations we can perform through the Web API on the DOM tree", i.e one may consider things like UI events and event loops part pf the DocumentObjectModel, but we're not talking about these here. – Kaiido Apr 25 '20 at 00:14
  • @Kaiido according to [this](https://v8.dev/blog/tracing-js-dom) link and [this](https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/bindings/core/v8/V8BindingDesign.md) link, the DOM is stored in form of C++ objects and the js objects are wrappers around those C++ objects. This means that the plain js objects are not the DOM objects. They are wrapper objects made available to javascript so that it can access the underlying DOM objects in C++. – chetan May 21 '20 at 18:22
  • @chetan How exactly React makes batching easier? Can you explain this concept? – noName94 Feb 13 '21 at 10:42
  • 2
    @anonymous_siva In React, the setState calls are asynchronous and they are [batched](https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous) together. Hence you don't need to worry about them being batched because React does that for you. – chetan Feb 13 '21 at 11:18
  • @chetan I really appreciate your research and links to the supporting resources you've provided. As a novice React developer I've been asking the same questions myself. I just wanted to add, that changes to the DOM are not dispatched in a single reflow, but rather properly batched (like reads are packaged and writes are packaged). This yields a better performance and most importantly (as you've mentioned) - we don't have to worry about it. – Andrii Lukianenko Apr 27 '21 at 15:34
  • @chetan correct me if I'm wrong. I think in vanilla javascript updation should me fast because using ids we can make the changes directly in DOM while ReactJs first perform the comparison between virtual DOMs and then update the actual DOM. – Shubham Oct 04 '21 at 02:11
  • @Shubham you are correct. – chetan Oct 04 '21 at 14:56
  • `Whenever anything happens in our app (click event, HTTP response, timeout), all the watchers are run` wait Angular could not be clever enough to only run the watcher specific to the event? Why does it need to run all the watchers? – Antoine Weber Mar 22 '22 at 12:05
  • @AntoineWeber Although I am not an expert on angular, I think running all watchers may be the only way. The watchers may not be bound to any events but to the value and so the only way to know if an event changed the value is by checking that value on each event. But someone who has knowledge on angular can tell better. – chetan Apr 05 '22 at 17:28
  • Outstanding explanation, I struggled with exactly the same question, and the background you provide here puts things in place for me, thanks! – Paljas Apr 30 '23 at 16:03
5

enter image description here

Once React knows which virtual DOM objects have changed, then React updates only those objects, in the real DOM. This makes the performance far better when compared to manipulating the real DOM directly. This makes React standout as a high performance JavaScript library.

Regarding the Batch Update:

React follows a batch update mechanism to update the real DOM. Hence, leading to increased performance. This means that updates to the real DOM are sent in batches, instead of sending updates for every single change in state.

The repainting of the UI is the most expensive part, and React efficiently ensures that the real DOM receives only batched updates to repaint the UI.

David
  • 15,894
  • 22
  • 55
  • 66
  • 7
    "How" does it make the performance better? Even without React the browser updates ONLY those objects that need to be changed. What did react bring to the table? – chetan Apr 16 '20 at 08:27
  • 1
    Didn't the bold text answer it? – David Apr 16 '20 at 08:28
  • 3
    regarding batch update, what exactly is the mechanism that react uses to "batch" the updates. How does it use the DOM api to send the updates in a single batch? – chetan Apr 16 '20 at 08:31
  • 2
    "then React updates only those objects, in the real DOM. This makes the performance far better when compared to manipulating the real DOM directly." It was said not in context of bold text but in context of updating only the changed objects. – chetan Apr 16 '20 at 08:32
  • @chetanraina Historically, back when React was introduced those optimizations in the browser were not as performant as they are today. Browsers later, with the introduction of the concept of a virtual DOM that React had introduced, created optimizations that mitigated the difference in performance. – user120242 Apr 19 '20 at 06:11
  • Also much of what enables batching can be said to be a direct result of the assumptions made in these tree updates. Most of the batching operations would be meaningless if you could not assume that the tree updates in the way pictured. – user120242 Apr 19 '20 at 06:13
  • @user120242 You mentioned that browsers created optimizations using virtual DOM. I have been reading about the V8 engine and didn't find any mention of a Virtual DOM. Can you cite a source that mentions any of the modern browsers using a virtual DOM ? – chetan May 10 '20 at 06:51
  • Except for the first sentence, your first paragraph makes no sense. With "this" you refer back to "React updating only those objects, in the *real* DOM". Then you say this makes the performance better than.. "manipulating the real DOM", which is the same! Except you say "directly", and doing it indirectly by definition adds effort on top. – Paljas Apr 30 '23 at 16:01