DEV Community

Cover image for Is VDom still faster?
Eckehard
Eckehard

Posted on

Is VDom still faster?

The idea behind a virtual DOM is clear: Build an in-memory representation of the dom and just apply the differences to the "real" DOM. This makes perfect sense on a machine, that is relatively slow. But is this still true today?

I entered the scene relatively late, just a few years ago. Before I was using compiled languages like C++, which are amazingly fast - most of the time. But - many people use mobile devices today, and a web browser may serve well to make your application platform independent. So, we checked out what is possible today.

We where amazed: Even though javascript is an interpreted language, running on chrome or firefox was exceptionally fast. Even memory intens applications seemd to perform very similar to what we whrere used to. The most amazing part is the HTML5 canvas element, that is very capable and creates high peformance antialiazed graphics in no time.

Another amazing part of the browser performance was the increadible well performing double buffering of screen content: Even rebuilding the greatest part of the screen did not even cause a flickering of the screen. It seems, the guys at google did an exceptional good job designing the chromium engine (Which is the same for other current browsers like firefox or opera too). In any case: there are strong tools in modern browser to hide unnecessary screen updates and present a smooth experience, even if the programming was not that good.

We did a lot of direct DOM programming and where always happy with the performance: It just seems that the world has changed today.

There is a simple question: Maybe it was right in 2012 to create a virtual DOM. But times have changed. A virtual DOM means: additional calculations are to be performed. Specially the diffing may cause tremendous load. And we know: There is a second machine behind (implemented in the browser), that also performs similary well to hide unnecessary screen updates.

My questions is: Do we still need a virtual DOM? IF the browser is hiding unwanted screen updates, what happens with the virtual DOM? Does it just create additional processor load. Is it possible that both systems may interfere in and unexpected way that makes things even worse?

Discussion (53)

Collapse
miketalbot profile image
Mike Talbot

VDOM has never been faster to execute than tightly hand crafted Javascript, but writing such code is a nightmare, and it's been much faster to build apps, component libraries etc with frameworks that use VDOM that still have great performance.

VDOM lets you have components which are re-runnable factories. When something changes you re-run the factory and it spits out VDOM nodes, these lightweight Javascript objects are then compared with the ones spit out last time and only the differences are applied.

Hand crafted reactive Javascript will always be faster than this, but at the cost of real complexity and much harder to reason with code.

Libraries like SolidJS work by making a much nicer reactive interface that still allows you to write only simple Javascript that knows which DOM elements to update when things change. In SolidJS components are never re-run after the initial mounting, instead reactive logic is used to change DOM attributes or nodes when things change in the state. This makes the code slightly more complex and introduces restrictions you must adhere to (for instance not using JS destructuring of props to components) compared to VDOM implementations.

So you have libraries like SolidJS and Svelte (more of a modified language) which are looking to create highly reactive components and libraries like Inferno, Preact, React etc etc that use VDOM.

In widely touted benchmarks (I borrowed a slightly out of date one of Ryan Carniato's examples below - Ryan is the author of Solid) the two closest to native Javascript are Solid and Inferno. Given how fast these are it's hard to imagine needing to write native JS because it would be so much harder to reason with. From this we can see VDOM can be crazy fast and reactive can be crazy fast and you know what? React isn't in that top ranking but actually it's also still fast enough for most people.

Performance Benchmarks for Javascript libraries

So do we need VDOM? Well I think that principles like reactivity (with Hooks in React and signals in Solid for instance) really help us construct our applications. I won't be going back to hand crafted JS for sure...

Collapse
efpage profile image
Eckehard Author

In fact, there is a lot of code out there that is a nightmare, but does this mean, we can´t write good code in Javascript? Do we need a virtual DOM because people are not willing to learn to write better code?

We have done some really nice applications recently, that are directly working on the DOM. Just plain event based Javascript. And it came out they work like a charm.

Collapse
miketalbot profile image
Mike Talbot

Well yep, I can see that could happen. I think you should certainly write the code you need. If you need the best possible performance or you can easily reason with your problem then that could well be the best way to go. It's all about the mental models you need to apply and at which level you should apply them right? Whatever gets the job done well enough, fast enough sounds good to me.

Thread Thread
efpage profile image
Eckehard Author

Sure, but there are reasons, why people developed frameworks like Angular or React years ago. For me it seems, that things have changed over the last 5 years and many of the reasons are gone. But the frameworks still are there.

Thread Thread
cswalker21 profile image
cswalker21

This is a very interesting idea to me. I’d like to hear more about it. Maybe you could write a new post about what changes over the past 5 years have made frameworks less vital. I would read it and click the heart.

Thread Thread
aliakakis profile image
Antonios Liakakis

@efpage these frameworks are still present because of the initial investment. Removing them takes time. Personally, I believe that today's front-end is overcomplicated. Using plain html pages plus a library like Knockout or Alpine for adding functionality is all it takes even for large applications. However, it is so embedded in the mindset of today's front-end devs, that only if its complicated it's proper, that most times the above suggestion falls into deaf ears.

Thread Thread
efpage profile image
Eckehard Author

Ten years back the browser landscape was still very inhomogenous. Creating Apps that could run on every computer needed tools like babel or forced to use a very limited set of commands that where common sense. And, Javascript was still a bit slow.

  1. About 5 years ago (maybe a bit earlier), companies managed to syncrhonize their development, so today we can be pretty sure that Javascript will run on every current browser. There are still some minor differences, but the common sense is pretty broad.

  2. Browser engines gained a lot of speed and possiblilties with HTML5, so the brower serves pretty well as a programming platform for all kinds of tools.

  3. Javascript developed a lot from a simple scripting language to a full features programming tools. In parallel it is very fast today so it can compeed with compiled code.

I´m not sure that 5 years ago it was a good idea to create native JS apps. Maybe it was possible, but still not easy. Today, things are different. We can use JS to write native apps and can be pretty sure they will run on more or less any machine.

I started the journey with my own framework DML recently, and the results are pretty amazing without any virtual DOM and bulky toolchain. The project is still in it´s baby shoes, but develops very nicely. So, I´m pretty sure that VDom is pure overhead. But as we have learned, a VDom is necessary for declarative code only. As DML relies on the OO paradigm, it plays very smooth on the real DOM.

Thread Thread
aliakakis profile image
Antonios Liakakis

Indeed 10 years the browser landscape was not the homogeneous we have now, which is mostly Blink layout engine everywhere. Nevertheless, I still remember even in that times releasing large projects with minimum effort.
VDOM is indeed an overhead and I agree. But with React dominating it will be difficult changing the mindsets of most devs to choose something different and in many cases better.
In my opinion it will be difficult going "away" from VDOM if React is not starting to slowly fade away from usage, which with the latest stackoverflow survey, its highly unlikely. Mostly, because as I said the majority of companies are deeply invested in using React in general either standalone or with Nextjs or Gatsby etc. Even if there is something better, and there is actually e.g. Svelte, they would argue against them citing a small community, sad but true for Svelte, and if ego was involved when choosing React, they will fight literally to uphold their choice, otherwise it will seem as if they chose the wrong horse for the race.
JavaScript has evolved amazingly from the time I actually remember it, 25 years ago, but I still cannot believe that most projects today are shunting simplicity for an over-complicated codebase.

Thread Thread
peerreynders profile image
peerreynders

but there are reasons, why people developed frameworks like Angular or React years ago.

During that time period and to this day people are chasing "emulating the native experience" in the browser.

But even back in 2015 PPK wrote Web vs. native: let’s concede defeat:

I’m sure there’s a lot more to discover here, and that we can rethink the web and its relation to natives based on the actual strengths and weaknesses of native and web instead of an idealised version of the web that isn’t really in touch with reality.

And more recently A clean start for the web (also Second-guessing the modern web, If not SPAs, What?) - the "application web" needs to stop chasing the native experience and devise leaner ways of successfully working with the most hostile development platform in the world.

Collapse
lito profile image
Lito • Edited

Chart updated with last versions, vue 3.2 and compared with vanillajs.

Collapse
ivanjeremic profile image
Ivan Jeremic • Edited

You need to read more about it! VDOM is faster on large trees, so if you give a huge tree svelte and react then react vdom will be faster on small tress svelte is faster

Collapse
peerreynders profile image
peerreynders

If the use of VDOM was about performance everybody would be using Inferno by now - especially if they don't need to support native web views.

So React's popularity has little to do with performant VDOM - because React's VDOM implementation isn't that fast.

Thread Thread
ivanjeremic profile image
Ivan Jeremic • Edited

Doesn't matter All I said is that on huge trees react vdom is faster. If you want to know the reason why we even need vdom the answer is xml/html is the worst language to parse(slow) thats the reason people switched from soap api's to rest with json because the format is faster to parse then xml so in order to never need it again the next ui language on the web needs to be a syntax like CSS maybe all features of HTML merged over to CSS? Never mind... all I can say in order to fix this HTML6 should not even come out.

Thread Thread
peerreynders profile image
peerreynders • Edited

If you want to know the reason why we even need vdom the answer is xml/html is the worst language to parse(slow)

That's misinformation; browsers are extremely efficient at parsing HTML and the DOM isn't slow - Live DB Monster Demo.


the reason people switched from soap api's to rest

REST is media format agnostic so JSON had nothing to do with it; in fact RESTful web services (2007) by Leonard Richardson, Sam Ruby used HTML - SOAP collapsed under the weight of the WS-Deathstar specifications - while only reinventing RPC in the process.

The thing I love about this model [REST] is that, as Sam says, it is of the Web, not over the Web.

I think of SPA(-frameworks) the same as I think of SOAP - neither of them are of the Web.


so in order to never need it again the next ui language on the web needs to be a syntax like CSS maybe all features of HTML merged over to CSS?

CSS was introduced in 1996 to remove the presentational aspects from HTML (1993) - they are not going to merge them after 25 years of separation.

Thread Thread
ivanjeremic profile image
Ivan Jeremic

If you really think HTML is faster to parse then CSS then I can't help you.

Thread Thread
peerreynders profile image
peerreynders • Edited

Who is comparing parsing speeds of HTML vs CSS?

Your original comment was about React (VDOM) being faster than Svelte on huge trees. SolidJS is faster than either of them and doesn't use VDOM.

Thread Thread
ivanjeremic profile image
Ivan Jeremic

Are there any tests or videos which can prove that Solid is faster on huge trees? I don't think it is.

Thread Thread
peerreynders profile image
Thread Thread
ivanjeremic profile image
Ivan Jeremic

Thats not what I asked you, I said show me prove that solid is faster on huge trees.

Thread Thread
peerreynders profile image
peerreynders • Edited

In what way are operations on 1_000 and 10_000 rows not representative?

A DOM document is a tree of DOM nodes - but perhaps those are not the trees you had in mind.

The tests do prove that React performance is "just OK" - but not really anything special to warrant a claim that a VDOM-based framework is needed for performance reasons.

Thread Thread
ivanjeremic profile image
Ivan Jeremic

I had exactly those in mind but this tests seem fishi to me.

Thread Thread
peerreynders profile image
peerreynders • Edited

Clone the repository and see for yourself…

Thread Thread
ivanjeremic profile image
Ivan Jeremic

I will lets look how the tests will be over 10k rows

Thread Thread
efpage profile image
Eckehard Author

Adding 10K rows is possibly not what most Websites do. And the initial question was not: who is the fastest number cruncher.

The question was: There are two systems in a pipeline to optimize the screen update:

  • A VDom
  • The browser engine

If the browser engine is slow and stupid, a virtual DOM will be helpful in most cases. But if the browser engine is fast and smart, maybe it does a better job than a VDom. The Chrome core was written in C++, which could be faster than Javascript.

AND: the browser knows, what is visible on the screen, while the VDom does not. So, from the point of user experience, there is a good chance that a browser does a better job if he does not need to wait for a slow VDom.

Caution slow

I think, the only way to find out is to perform some tests with and without VDom active.

Thread Thread
ivanjeremic profile image
Ivan Jeremic

Ok I need to say I tried Solid JS today and my mind is kind of blown away.

Thread Thread
efpage profile image
Eckehard Author

Could you give us more information about your background and expectation? What precisely blew your mind?

Thread Thread
ivanjeremic profile image
Ivan Jeremic • Edited

My background? Eckhard is not the type of guy who can ask questions like that.

Thread Thread
efpage profile image
Eckehard Author

What´s your problem? Just tried to understand your previous answer.

Collapse
marzelin profile image
Marc Ziel • Edited

VDOM is just an implementation detail. The real goal here is declarative programming.

DOM is imperative and statefull so we need some framework/library to transform our declarative code into imperative commands that DOM provides.

This translation can be done entirely at runtime with something like VDOM diffing but most of the work can be done ahead of time (at build/compile time) and that's the current trend.

Relying on the DOM for declarative updates (essentially by recreating the DOM tree whenever there's a change) isn't a good idea since the DOM wasn't created to work like this so there are lots of problems with this approach.

Collapse
peerreynders profile image
peerreynders

The real goal here is declarative programming.

The issue is that React specifically and VDOM generally imposes a "run time cost" for the developer convenience of "declarative design".

Given that the browser is the native application and JavaScript is just (the highly optimized) automation language to coordinate user interaction, client-side development really needs a more lean and mean mindset (The Mobile Performance Inequality Gap, 2021).

Given that situation developer conveniences should not impose run time costs but instead rely on design time tools to compile to an effective machine representation - i.e. code that manipulates the DOM efficiently and effectively without relying on runtime tricks like VDOM.

In a way SolidJS is a move in that direction but there is room for improvement tooling-wise - the more work can be done at design/compile time the better.

The other issue is that the claim that "React is declarative" is widely publicized but largely unrealized in code bases and wouldn't hold up under close scrutiny. People see JSX tags and say - "Ooohh! It's declarative" ignoring the all the imperative JavaScript code surrounding and permeating it.

CSS is declarative and many developers hate it.

Collapse
marzelin profile image
Marc Ziel • Edited

When you write a React component you're writing imperative code that generates declarative code (a description of a view a.k.a VDOM). But what's the value in this?

An app can be thought of as a state machine: you start at 0 and from there you can typically go to any other state and from that state to some different arbitrary state. For each of these states you have to create a UI view that represents a given state.

Doing this naively you'd have to create code that updates the view from all possible state changes. If you have just 0 (initial) + 3 states it gives 3 + 3 * 2 = 9 (n^2) code paths you have to create and maintain.

But you can be smart about it and when there's a change from, let's say, state 3 to state 1, instead of going there directly, you'll go state 3 -> state 0 -> state 1. In other words, when there's a state change you destroy the view for state 3 and start building the view for state 1 from 0. Now you have all possible state changes covered with just 3 (n) code paths.

Unfortunately, destroying and rebuilding the DOM tree at every state change isn't a good idea because DOM is stateful and isn't optimized to work in this manner.

That's where React comes in. With React you need to create a component that can generate a description of a view for any given state and React will take care to efficiently update the DOM from arbitrary state x to arbitrary state y by comparing the descriptions for the view for both states that your component generated.

So, once again: it's not your component code that's declarative in React - it's the code that your component generates. In other words, React is declarative in the sense that React takes declarative description of the view (that your imperative component generates) and instructs DOM to render or update the view so that it matches the description. Reconciliation process can be thought of as a translation layer that takes declarative code and returns imperative instructions required to update the DOM to the described state.

Declarative approach is a must when you want to build a complex UI because otherwise your code would quickly became unmanageable (for just 10 states you'd have 10^2 = 100 possible code paths) even though destroying the view and rebuilding from scratch is obviously slower than smooth transition from one state to the other.

Technically you could destroy and rebuild the DOM on every change (and force browser vendors to optimize for that) but as you mentioned a better way is to optimize code at build time and React team is aware of that:

Additionally, React has been out for about five years, and we want to make sure it stays relevant in the next five years. As Svelte, Angular, Glimmer, and others show, ahead-of-time compilation of components has a lot of future potential. Especially if it’s not limited to templates.

Thread Thread
efpage profile image
Eckehard Author

Engineers have proved that bumblebees can't fly! Luckily the bumblebee does not know this....

Thread Thread
lito profile image
Lito
Thread Thread
peerreynders profile image
peerreynders

The original statement:

The real goal here is declarative programming.

… an excerpt from Wikipedia

… a style of building the structure and elements of computer programs—that expresses the logic of a computation without describing its control flow.

Given that definition SQL seems like a good example of declarative programming. A statement expressed in Data Manipulation Language "declares" the nature of the data desired while the manner in which it is obtained is left entirely up to the RDBMS engine.

Now based on that example writing components containing imperative code to generate SQL statements (DML) is not declarative programming.

From the React landing page:

Declarative - React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.

So "declarative approach to building UIs" is double speak for "I can't be bothered dealing closely with the actual DOM - ever."

as you mentioned a better way is to optimize code at build time and React team is aware of that

I wouldn't hold my breath - Compiler Optimization Umbrella #9223.

Also Scheduling:

However the freedom to do something like this is why we prefer to have control over scheduling, and why setState() is asynchronous. Conceptually, we think of it as “scheduling an update”.

The control over scheduling would be harder for us to gain if we let the user directly compose views with a “push” based paradigm common in some variations of Functional Reactive Programming. We want to own the “glue” code.

i.e. the design philosophy prefers run time control over compile time commitment.

Thread Thread
marzelin profile image
Marc Ziel • Edited

From the React landing page:

Declarative - React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.

So "declarative approach to building UIs" is double speak for "I can't be bothered dealing closely with the actual DOM - ever."

I've just written a post about it here

But you can have stateful function components with hooks and using hooks makes a component impure, right? Technically yes, but in practice not really as I touched upon here.

Collapse
efpage profile image
Eckehard Author

That makes perfect sense. Thank you very much.

Just, I tried to find out, which paradigm REACT follows, but it seems, people have different oppinions on that point.

Collapse
marzelin profile image
Marc Ziel • Edited

JavaScript is multi-paradigm and so is React. Locking yourself to just one paradigm for no good reason isn't practical.

Hooks allow to have stateful function components. With just stateless components you'd have to re-render the whole React tree on every change.

Hooks technically make components impure, but in practice they can be thought of as pure - hooks are there just for developer convenience.

A pure version of a "stateful" function component would require passing in state as argument and you'd have to return it along with effects and the rendered element tree. It would look pretty ugly.

Thread Thread
efpage profile image
Eckehard Author

Some years ago we would have named this "spaghetti code", today it´s called "multi-paradigm". I should remember this...

Thread Thread
marzelin profile image
Marc Ziel • Edited

Are you suggesting that people who write React write spaghetti code? That was rude and haughty.

Hooks despite looking weird allow you to collocate all code for a given feature in one place (you can't do it with class components) so the code is more organized and manageable - it's the opposite of spaghetti code.

You shouldn't bash on features just because they didn't exactly fit the concepts you learned some years ago.

Conceptually React follows declarative functional paradigm (UI = fn(state)) but at implementation level it makes compromises when it's beneficial from performance or DX standpoint since JS isn't a purely functional language.

Thread Thread
efpage profile image
Eckehard Author

Sorry, i didn´t want to insult anybody. But we see a lot people praying about the welfare of functional programming these days and that people using other concepts are wasting their time with complicated and inefficient code.

Above, we learned, that "The real goal (of VDom) is declarative programming. DOM is imperative and statefull...", which means, that it would better apply to a imperative and stateful paradigm (-> OO). So, in this case, the "declarative approach" has some overhead.

It seems, there are more cases where the brave new world of functional programming has a high price, which makes a solution like a "hook" very appealing. It´s fully ok, even if people start using goto again. But then they should stop praying about "pure functions as a solution to everything" (or bashing people about "concepts they learned some years ago.")

Even if a hook is well organized, using a hook in a pure function just makes the function impure, so "Multi-paradigm" is just a nice wording for that.

Thread Thread
marzelin profile image
Marc Ziel • Edited

Declarative approach isn't used as an optimization technique but to facilitate development of complex UIs that are both efficient and manageable as I explained here.

Even if a hook is well organized, using a hook in a pure function just makes the function impure

Using a hook actually allows a function to stay pure because it takes care of code that produces side-effects so that you don't have to call it inside of the function.

Compare the code:

function impure() {
  console.log("side effect");  // state of the world changed during the execution
}

function pure() {
  // this function just returns another function so it's still pure
  return () => console.log("side effect"); 
}

function Component() {
  useEffect(() => { // takes this side-effectful code out of the component
    console.log("side effect"); 
  })
  return "Still pure. Yay!"
  // technically it's impure since we call `useEffect` to register an effect
  // but we could theoretically do the same by returning it:
  // return { renderTree, effects: [logger] }
}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
peerreynders profile image
peerreynders • Edited

Using a hook actually allows a function to stay pure because it takes care of code that produces side-effects so that you don't have to call it inside of the function.

That statement is bordering on self-delusion.

Haskell Programming from first principles — Chapter 29: IO - 29.6 Purity is losing meaning:

It’s common at this time to use the words “purely functional” or to talk about purity when one means without effects. This is inaccurate and not very useful as a definition …

Semantically, pedantically accurate
… What was originally meant when describing a pure functional programming language is that the semantics of the language would only be lambda calculus. …

Referential Tranparency
Referential transparency is something you are probably familiar with, even if you’ve never called it that before. Put casually, it means that any function, when given the same inputs, returns the same result. More precisely, an expression is referentially transparent when it can be replaced with its value without changing the behavior of a program.

One source of the confusion between purity as referential transparency and purity as pure lambda calculus could be that in a pure lambda calculus, referential transparency is assured. Thus, a pure lambda calculus is necessarily pure in the other sense as well.

The mistake people make with IO is that they conflate the effects with the semantics of the program.

So even if we ignore side effects, stateful function components violate referential transparency which is part of what pure functions have to guarantee.

Hooks (useState() in particular) give the stateful function components access to React-managed mutable state — so same inputs (i.e. function arguments), same results is not guaranteed. That state is stored inside a closure on a per component instance basis. Object instances and closures are duals of one another.

All hooks did was replace this.state, this.setState() with useState(). Stateful function components are no different than a render function on class-based components - they simply use useState() instead of this.setState().

So what React has accomplished is that everyone can still work in their comfort zone of class-based object orientation while pretending to be functional — Look ma, no classes, just functions.

Thread Thread
marzelin profile image
Marc Ziel • Edited

Hooks (useState() in particular) give the stateful function components access to React-managed mutable state — so same inputs (i.e. function arguments), same results is not guaranteed.

When you see Haskell do notation:

do { putStr "Hello"
   ; putStr " "
   ; putStr "world!"
   ; putStr "\n" }
Enter fullscreen mode Exit fullscreen mode

you might say: Look ma, imperative statements and side effects - Haskell is like Java. But there is much more than what meets the eye, isn't it? do notation is just syntactic sugar that translates directly to monadic operations.

Dan Abramov:

Finally, if you’re a functional programming purist and feel uneasy about React relying on mutable state as an implementation detail, you might find it satisfactory that handling Hooks could be implemented in a pure way using algebraic effects (if JavaScript supported them).

again Dan Abramov:

conceptually you can think of useState() as of being a perform State() effect which is handled by React when executing your component. That would “explain” why React (the thing calling your component) can provide state to it (it’s above in the call stack, so it can provide the effect handler). Indeed, implementing state is one of the most common examples in the algebraic effect tutorials I’ve encountered.

Let's do some example to see what's this all about.

Having this component:

function Component() {
  const [name] = useState();
  const [greeting] = useState();
  return <p>{greeting}, {name}!</p>
}
Enter fullscreen mode Exit fullscreen mode

we can see that it escapes outside its boundaries twice by calling useState. useState call yields control to React and React passes in some data and then gives control back to the component. Hm... yielding and then taking back control... It seems like a generator, so let's make it a generator:

function* Component() {
  const [name] = yield;
  const [greeting] = yield;
  return <p>{greeting}, {name}!</p>
}
Enter fullscreen mode Exit fullscreen mode

It looks a bit weird but when you look at how it's used:

const iter = Component();
iter.next()
iter.next(["Greg"])
iter.next(["Hello"])
Enter fullscreen mode Exit fullscreen mode

You'll see that it's just a curried function:

function Component() {
  return ([name]) => {
    return ([greeting]) => {
        return <p>{greeting}, {name}!</p>
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

That can be shortened to:

const Component = () => ([name]) => ([greeting]) => 
    <p>{greeting}, {name}!</p>
Enter fullscreen mode Exit fullscreen mode

As you can see it's just a pure function - the only thing that is special about it is that it can be called in three separate steps and this doesn't break its purity. Yielding to the caller for more data in the middle of operation is completely legal - that's what IO Monad is doing under the hood.

Summing up, hook notation is like do notation - it doesn't make the code impure; it's just an abstraction that makes working with the code easier.

From React design principles:

React is pragmatic. It is driven by the needs of the products written at Facebook. While it is influenced by some paradigms that are not yet fully mainstream such as functional programming, staying accessible to a wide range of developers with different skills and experience levels is an explicit goal of the project.

and:

We try to provide elegant APIs where possible. We are much less concerned with the implementation being elegant. The real world is far from perfect, and to a reasonable extent we prefer to put the ugly code into the library if it means the user does not have to write it. When we evaluate new code, we are looking for an implementation that is correct, performant and affords a good developer experience. Elegance is secondary.


So what React has accomplished is that everyone can still work in their comfort zone of class-based object orientation while pretending to be functional

It's the other way around: React uses functional concepts that provide real benefits but serves them in a digestible form suited for plain JavaScript.

Thread Thread
peerreynders profile image
peerreynders

Haskell is like Java

The Curse of the Excluded Middle — "Mostly functional" programming does not work.

The ease with which you can define reusable abstractions over effects is why people often call Haskell the world's best imperative language.

do notation is just a language feature, not the defining characteristic of Haskell — being firmly based on lambda calculus is. The do notation is just imperative sugar over a functional core. Haskell doesn't pretend to be imperative.

Compare that to React which has a somewhat functional appearance (skin) over an essentially imperative core. React pretends to be functional.

In many ways React's true nature is best revealed by createReactClass (formerly React.createClass).

var specs = {
  render: function() {
    return React.DOM.span(null, "I'm so custom");
  }
};

var Component = React.createClass(specs);
Enter fullscreen mode Exit fullscreen mode

Even the naming is awkward — it really should have been React.createComponent - but to seem more current they went with Class in ES5 to seem more respectable in context of a OO mainstream.

The parameter being passed is a Crockford specification object:

  • React manages the component instances and their state that render against the VDOM.
  • To register a custom component one has to supply a set of functions (the specs specifying the behaviour for the custom component instances), each function handling one particular life cycle event. The render handler is mandatory while all the other events have default handlers. These life cycle handlers get access to React-managed component instance state via this.props and this.state (which is mutated via this.setState()) when they are invoked.

Even with hooks React's fundamental character is largely unchanged only that now there is only the render function (making it seem more functional) which in it's body has to somehow register/manage the handling of the other life cycle events.


you might find it satisfactory that handling Hooks could be implemented in a pure way using algebraic effects.

Algebraic Effects are reflected in the type system. That's why in Haskell input/out happens inside of the IO monad. Algebraic effects have no reflection in the type system of JavaScript, TypeScript or Flow - so there is no rigor behind it, no containment or isolation, and it's just the Wild, Wild West.


As you can see it's just a pure function

Generators are not pure functions. They are based on an internal mutable object/closure which means that they are not referentially transparent and therefore not pure.

And the whole "it's just a curried function" is just a sleight of hand. Currying solves a very specific problem.

Haskell Programming from First Principles — Chapter 1: Anything from Almost Nothing - 1.5 Beta Reduction, Free Variables

Each lambda can only bind one parameter and can only accept one argument. Functions that require multiple arguments have multiple, nested heads. When you apply it once and eliminate the first (leftmost) head, the next one is applied and so on. This formulation was originally discovered by Moses Schönfinkel in the 1920s but was later rediscovered and named after Haskell Curry and is commonly called currying.

i.e. to have multi-argument functions in an environment that only allows each function to have a single argument. useState() doesn't address that problem. useState() serves to have former component invocations affect later invocations and more importantly allows the "outside world" to have an effect on the component instance state. Even if this happens somewhat under the control of React there is no rigidly enforced compile and run time contract like there is with, for example, the IO monad.


Summing up, hook notation is like do notation - it doesn't make the code impure; it's just an abstraction that makes working with the code easier.

I disagree, the comparison between do notation and hooks is seriously flawed at best. Hooks make functions impure and are just a React specific mechanism for code organization and perhaps reuse - that's it and in that capacity they may be considered an abstraction of some sort.


React uses functional concepts that provide real benefits but serves them in a digestible form suited for plain JavaScript.

I continue to disagree - React also claims to be a "not a framework" when in fact it's manner of operation belies that claim; i.e. one has to take the claims made inside the React documentation with a bunch of salt.

Thread Thread
marzelin profile image
Marc Ziel • Edited

The do notation is just imperative sugar over a functional core. Haskell doesn't pretend to be imperative.
Compare that to React which has a somewhat functional appearance (skin) over an essentially imperative core. React pretends to be functional.

When you compile your pure Haskell code the resulting machine code is imperative and effectful. What's more, Haskell compiler (GHC) is partly written in imperative languages like C. From your strict point of view that probably make it an impostor that pretends to be functional while having imperative core. ;)

React doesn't pretend to be functional. It clearly states that it is influenced by functional programming concepts and this is reflected in its API.

React can't have a fancy functional syntax that could please every purist because it isn't a compiled language but a view library.

Yes, a library. A library is just a bunch of code providing some functionality that can be shared and used easily in many applications. React fulfills that definition. It also fulfills the definition of a framework so you can call it a view framework if you wish, but "view framework" name is kind of already taken by another project.

But if you see a sentence:

React is not a framework like Angular but just a library for building user interfaces

and all you get from it is that "React is not a framework" then it's not the sentence that's wrong - it's your interpretation of the sentence.

In many ways React's true nature is best revealed by createReactClass (formerly React.createClass).
Even the naming is awkward — it really should have been React.createComponent - but to seem more current they went with Class in ES5 to seem more respectable in context of a OO mainstream.

It's called createClass because it creates a class. It was needed because creating creating classes in ES5 is really awkward.

useState() serves to have former component invocations affect later invocations and more importantly allows the "outside world" to have an effect on the component instance state.

useState() serves the same purpose as State Monad. The difference here is that state management is pushed out of your Haskell code and managed outside (Haskell compiler does the dirty work). React is just a bunch of JS code so the state is still kept within the realm of JavaScript. But no, "outside world" cannot directly change the state - even a component can't do it directly - all state changes must have to go through React setState call and only React can change it. React guards the access to component state in similar way as Haskell guards access to State Monad.

Even if this happens somewhat under the control of React there is no rigidly enforced compile and run time contract like there is with, for example, the IO monad.

Yes, it's not Haskell, it's JS so you still can do some weird stuff but React is rigid enough and doesn't give you many options to mess things up.

Hooks make functions impure and are just a React specific mechanism for code organization and perhaps reuse - that's it and in that capacity they may be considered an abstraction of some sort.

Yeah, you can be very picky about functional programming and only recognize the following definition:

Functional programs contain no assignment statements, so variables, once given a value, never change. More generally, functional programs contain no side-effects at all. A function call can have no effect other than to compute its result.

But this makes you like this:

The functional programmer sounds rather like a mediæval monk, denying himself the pleasures of life in the hope that it will make him virtuous. To those more interested in material benefits, these “advantages” are totally unconvincing.

If I can have more or less the same benefits as in a pure functional language but without having to switch from the most universal and ubiquitous language in the world that's a pretty good deal for me.

Thread Thread
peerreynders profile image
peerreynders • Edited

When you compile your pure Haskell code the resulting machine code is imperative and effectful.

And during the compilation process the code is verified to comply with numerous constraints that permit the compiler to make some very specific and beneficial assumptions about the code.

React doesn't pretend to be functional.

I think here we have to differentiate between React and the React community. Now clearly React itself can't be responsible for the entire React ecosystem - but often the wording in the documentation or the statements by core members encourage certain notions in the community.

React's mental model is often oversimplified as view = fn(state) — which is clearly functional as it implies view generation from state through transformation by a pure function.

React actually doesn't work that way — there are two sources of state — props which is state as handed down from the owner component to the nested component and state which is "persisted and managed" by React on behalf of the component instance. So while components do generate the view they also can change state in the process - i.e. components are not enforced to be referentially transparent or in web terms idempotent. So React cannot grant the typical benefits or guarantees that a typical FP environment can.

React's reliance on immutability further feeds into the image of being functional.

Then there is the fact that the community often refers to Function Components as Functional Components.

So when it comes right down to it, one has to wonder how much of "hooks and stateful function components" actually had to do with necessary innovation or whether or not the whole "functional image" is about keeping React trendy within the web development community at large.

but "view framework" name is kind of already taken by another project.

React predates Vue by about a year and despite its name Vue doesn't get to monopolize an entire tool category by virtue of how its name sounds.

all you get from it is that "React is not a framework" then it's not the sentence that's wrong - it's your interpretation of the sentence.

but I also think it’s not a framework.

It's not my interpretation, it's an active image being defended by a large portion of the React community. A framework, due to inversion of control, exerts a significant amount of design pressure on the structure of the "client side architecture". Calling React a library is disavowing the influence that React has as evidenced by countless sources describing React as unopinionated. Just because React is less constraining than Angular does not imply that it is "unopinionated" — it's just a matter of degree — React is very much opinionated, just not as much as Angular.

It was needed because creating classes in ES5 is really awkward.

Did it return a constructor function? A factory function for component instances? At best it's bad naming because createComponent would have been more apt — at worst it's trendy naming because in 2013 "real programmers" used class-based object-orientation — not just plain functions and objects.

Generators are pure functions as long as you don't mutate state within them.

Most generators used in JavaScript are not pure. And JavaScript has no controlled runtime support for lazy evaluation, so lazy evaluation is emulated with mutable state inside the function's closure.

useState() serves the same purpose as State Monad.

"Purpose" perhaps but again without committing to any constraints. In order to preserve referential transparency functions operating within a state monad have a signature of (state) => [state, result]. React function components could have easily adopted a similar signature. One benefit would have been that it would be trivial to micro test components without having to provide an entire "React environment" for the component to run in.

And that's just the point — React does just enough to pander toward maintaining a "functional image" but doesn't commit to the necessary constraints that would afford developers the actual benefits of many FP techniques.

But no, "outside world" cannot directly change the state

Of course it can - that is the whole principle of how third party libraries like Redux integrate themselves with the React component tree — especially since the introduction of hooks. And even before that the "outside world" invaded the component tree via Higher Order Components (connect, Provider pattern) which are all still "React Components".

React guards the access to component state in similar way as Haskell guards access to State Monad.

There is no "guarding" of any constraints.

It maintains control for the purpose of detecting changes to component instance state and to maintain full control over view reconciliation and scheduling.

If I can have more or less the same benefits as in a pure functional language but without having to switch from the most universal and ubiquitous language in the world that's a pretty good deal for me.

So the the Curse of the Excluded Middle is just Erik Meijer ranting again? And he can rant.

The functional programmer sounds rather like a mediæval monk,

Frankly, I couldn't care less if React was functional or not — but I am getting tired of the pretentiousness exuded by it and a significant part of it's community.

For example wickedElements is not functional in nature at all but it is brilliant in the way it leverages JavaScript and the DOM ("the platform"). It is what it is and it is good at at it.

Meanwhile there seems to be a cult-like following around React unwilling to examine and evaluate the tool objectively but instead is willing to propagate half-truths:

  • React is not a framework — of course it is.
  • React is unopinionated — due to inversion of control it has a definite opinion, just not as severe as Angular.
  • React is functional in nature — it uses functions. So does the C language, neither of them is functional in the FP sense.
  • React is declarative — it manipulates the DOM so you don't have to. Meanwhile there are no benefits of declarative programming, it's just the usual BBoM; i.e. it's just an excuse to avoid learning how to manipulate the DOM or use something else that actually leverages the browser's strengths.
Collapse
lexlohr profile image
Alex Lohr

A virtual DOM has still some benefits. It's easier to mutate than actual DOM and the abstraction allows for some sophisticated dev tools.

However, when it comes to performance, the overhead of a vDOM becomes problematic in terms of scalability. While interference between reconciler and layout engine are mostly taken care of by batching the changes, if the reconciliation takes longer than a render frame, things become jerky fast.

In any case, going sans vDOM as well as using it is no silver bullet, because the bottle necks are usually more in the reactive flow, which is why there are state management libraries like immer or frameworks like Solid.js that allow for fine-grained control over it.

Collapse
132 profile image
Yisar • Edited

As it happens, I have experience in both.
Frankly, no matter what it is, I can optimize it to the mechanism, but the principle is different.
The main work of vdom is to create a data structure, and then make an algorithm based on the data structure.
We can't change the DOM tree, we can only reuse it as much as possible.
This is the difference between the two, but it is undeniable that they can both fastest.

Collapse
ptejada profile image
Pablo Tejada

There is a typo cpable

Collapse
efpage profile image
Eckehard Author

Thx!

Collapse
siddharthshyniben profile image
Siddharth

I'm guessing VDOM is still faster, but it isn't noticeable enough to use it (I once did a benchmark and I found that VDOM is ~0.00001612903ms faster).

Collapse
efpage profile image
Eckehard Author

As VDom adds some operations before an action is performed in the DOM, it cannot be faster - except you can save some unnecessary operation. So any benchmark depends much on your task.

But we see today, that modern browsers can remove unnecessary operations as well, so the questions is: Who performs better.

Collapse
efpage profile image
Eckehard Author

That is really an impressiv presentation of Sindre Osen Aarsaether, that just gets it to the point. Really amazing!