Hacker News new | ask | show | jobs
by seer 1798 days ago
Well react itself is just the virtual diff algo really.

How to change this page to this other page as fast as possible, if you don’t know or don’t care to know that other page.

That second qualifier is really important. As long as you have 2-10 screens and know the transitions by heart, nothing stopping you from hand rolling js. But once you start adding states things get tricky really fast, as transitions grow exponentially.

I’ve built some very complicated apps with vanilla js back in the day, and we had ways of dealing with things like that. We called it “reloading the page” where you start from 0 with your state. Kinda like restarting windows to fix it, rather than figuring out whats wrong.

And you could get quite far that way. 37signal’s basecamp was like that - an html app with vanilla js sprinkled throughout. Worked great.

But there is a limit in complexity. JS and html are great for building websites, but if you want to build an actual application, you need to be really clever and accept a lot of limitations. React just lifts the ceiling of what you can do, without being all to complicated.

And you can use the technics of react without react itself too, once you understand what it is all about - https://github.com/ivank/vanilla-teuxdeux

1 comments

Why do the virtual dom thing at all? Just remember the locations you need to update and update only them, like svelte or lit-html.
Why is it relevant? The point is you are not the one needing to remember.

One day React could change from using Virtual Dom to the Svelte way and most user of library wouldn't notice.

The point of the article is that React remember where to make the modification for you.

It seems like it's a non-effective way of doing it. In large DOM trees you need to manually interfere to make them update fast because (1) building even the virtual DOM and (2) then comparing this to the real DOM become slow => i.e the "React way" doesn't actually scale. Perhaps for a product guy this doesn't matter, but for an engineer it just feels wrong. Why not solve the real problem?
Because the real DOM exists in a separate world where you have to cross a trust boundary. The vdom is an implementation detail of React because the real DOM is too slow to traverse to diff on every render. Think of it like this.. jquery is a wrapper around a querying API, so it’s like running a sql query that can be indexed in a b-tree or a hash map, because you query by id or class or some other attribute. React on the other hand needs the entire state, which is equivalent to a select *.

If you say why does react need the whole state, it’s because React is a translation layer between an immediate mode representation and a retained mode system (the DOM). To illustrate this in a basic way, think of a checkbox. It is an entity in the DOM, but it is also retaining state itself (there is a Checked property that persists as long as it is set, so you know it has state). The only way to know if that checkbox is checked or not is to query the DOM to get the checkbox entity, or to “control” the rendering of the component so you can always derive the checked state from somewhere in your code, and force its state to match. React controlled components are a way to express unidirectional state from your code to the UI.

What situation were you in where vdom was the problem? I’d love to see an example. It is likely that vdom was not the performance issue, something was wrong with the implementation and causing a full re-render. Could you link a sandbox?

I understand why it's convenient to derive the UI from the state, no need to explain that.

But what react does is: for any change in state it creates the entire virtual DOM" regardless of if there was a single checkbox change and everything else remained the same. Then it compares this whole tree* to the real DOM, and then only replaces the parts that changed. The "win" of react is that instead of rendering the entire real DOM again and again it just renders something that is not seen and then swaps out the changed parts in the real DOM.

Why not short-circuit from changes in state to real DOM instead?

I think any google sheet like app would do: you type into some box, this changes state which triggers the creation of the virtual DOM for the entire sheet. Then it compares this entire sheet to the real DOM, finds that only one cell has changed and swaps that out. There will be one entire virtual DOM per character typed. Which is why I'd presume most if not all such applications need to add custom code (using `shouldComponentUpdate`) to fix this scaling issue.

wasn't this done because e.g. updating manually a thousand elements one by one was shown to be, like, orders of magnitude slower than updating the whole thing ?
No, not at all.

On the one hand it is rare that you'd need to update a thousand elements at once one by one.

On the other hand, and more importantly, the problem that Virtual DOM aims to solve is not that one. Instead, what it wants to avoid is not "updating a thousand elements" but "replacing large sub-trees in the DOM unnecessarily".

That is, Virtual DOM competes, mostly, against doing something like...

    someElement.innerHTML = '...<a large piece of HTML>...'
The main concern of doing this is not so much that the performance is bad or not, but that it is intrinsically wasteful. That is, it [almost always] unnecessarily forces the browser to rebuild a part of the DOM and re-render a part of the page... just to change but a few values.

This is written in that way because it's easy to write it like that. But, being such a wasteful approach, it does end causing performance concerns.

Going from this, you can use various approaches. A Virtual DOM basically let's you manage your [virtual] DOM fragments as you were already doing but then inserts itself as a middleman to avoid doing those unnecessary DOM replacements and only modify what needs to be modified.

There are two things you need to notice here: First it does add some additional processing an calculation -"the diff"-. Second it still, because there is no other way to do it, uses the same DOM API calls you would use if "manually updating things".

So now you can balance this to see if it's worth it: On the one hand there's a cost -the added calculations-, on the other hand there is a gain -modifying only smaller parts of the DOM-. But be aware that this gain is compared not against "updating many elements" but against "rebuilding parts of the DOM when you don't need to". The distinction is rather important because if, suppose, you actually need to update the content of say a thousand <td>s in a table, your virtual DOM library will still need to do exactly that, it will do so by using the DOM API -because there's no other way-, and any additional processing it does will be added on top of doing that.

So... bad option: build a whole...

    <table><tr><td>A</td><td>B</td></tr><tr><td>C</td><td>D</td></tr>...</table>
...every time and dump it all into the DOM element; let the browser do all the work and live with the bad performance you get from having easy to write but wasteful code.

Virtual DOM solution: Still write easy to write and wasteful code but apply something in between that reads your "whole block" and finds what actually needs to be done and then does only that.

But it's not the only solution. Another solution is to just don't write that wasteful code in the first place. Instead of building large but mostly unchanged blocks and dump them, only modify what needs to be modified. Of course, it usually means that your code has to keep track of an additional bunch of things -i.e. where in the DOM does each piece of information go-, but this is where those other alternatives mentioned, like Svelte or others, may come into play.

The conclusion from all this is that:

a. No, a virtual DOM is not inherently faster than doing updates manually

b. A virtual DOM provides some gain by not doing unneeded updates of DOM branches. If you were doing those, using a virtual DOM may speed things up.

c. A virtual DOM still needs to do the DOM modifications with exactly the same API you would/could use manually so there's no gain there.

> On the one hand it is rare that you'd need to update a thousand elements at once one by one.

Is it ? I come from the desktop app world and that would frankly barely register above "toy project" ; my experience is more around a few tens / hundred of thousands objects that can change at once (with of course the UI framework only redrawing what needs redrawing).

> There are two things you need to notice here: First it does add some additional processing an calculation -"the diff"-. Second it still, because there is no other way to do it, uses the same DOM API calls you would use if "manually updating things".

I have a hard time understanding how, say, calling 500 methods per second on a DOM object, e.g. `label.innerText = someSensorValueComingFromWebsocketsVeryFast;`, which needs to trigger various events, callbacks, etc... every time could possibly be faster than modifying a pure JS object at the "incoming message rate" and then blitting that object at the screen refresh rate or something similar ?

> I have a hard time understanding how, say, calling 500 methods per second on a DOM object, e.g. `label.innerText = someSensorValueComingFromWebsocketsVeryFast;`, which needs to trigger various events, callbacks, etc... every time could possibly be faster than modifying a pure JS object at the "incoming message rate" and then blitting that object at the screen refresh rate or something similar ?

Updating a value on the DOM 500 times per second just because you can read it 500 times per second falls, again, in the category of writing wasteful code just because it's simpler. i.e. "don't care about performance, just let the browser work".

If you compare code which is initially bad, then sure, a lot of "solutions" will be better than that.

The appropriate comparison for evaluating the value of using a virtual DOM should not be about that, but only about the part you describe as "blitting the object to the screen". You have the data, no matter how, where, or even -to some extent- how often, and you want to put it on the screen.

> The appropriate comparison for evaluating the value of using a virtual DOM should not be about that, but only about the part you describe as "blitting the object to the screen". You have the data, no matter how, where, or even -to some extent- how often, and you want to put it on the screen.

But the solution to the problem which is

    how to go from 
      inputs (which you don't have any control over) 
    to 
      "You have the data, no matter how, where, or even -to some extent- how often, and you want to put it on the screen"
will pretty much look like a vdom, no ?
> That is, it [almost always] unnecessarily forces the browser to rebuild a part of the DOM and re-render a part of the page... just to change but a few values.

That's a mere implementation detail. In practice, letting the browser re-render a single page chunk using its internal implementation will almost always be faster than using JS code to serially update any number of DOM elements. VDOM diffing is a kludge to make DOM manipulation in pure JS usable, it's not actually going to be faster than relying on the browser's own rendering. The biggest problem with the innerHTML is going to be generating the actual HTML, but one can most likely use WASM to speed that up.

Every time I see d3 stuff, it's pretty fast.
I don't think so. I believe it's because without a virtual DOM and a diff, how would react do rendering at all? The whole point of react is you just have a functional "render" method, so you need to render _somewhere_ (and it can't be the DOM for a variety of reasons, eg. deleting and rebuilding the whole DOM would be slow and break things like focus).

Svelte is different and doesn't export a render function -- the component knows what DOM nodes need to change for a given prop/reactive var change, so it doesn't need a virtual DOM (and can be much faster as a result)