The fundamental magic of react is that it lets you write code that renders O(n) UI states in a functional manner (i.e. by returning the HTML you want) rather than O(n^2) UI state transitions in a mutable manner (by mutating the page via nested callbacks), and somehow makes it kind of fast-ish
People love complaining about React, but I don't know if they remember the pre-React world where you had to write the code to manage O(n^2) UI state transitions via recursive mutating callbacks. Simple things like "have a the list of photos shown always kept in sync with a label showning count of photos" was incredibly error prone, and the complexity of implementation went up quadratically with the complexity of your app (due to the n^2 state transitions you needed to worry about!)
Today React is not the only one to do this O(^2) to O(n) complexity reduction, but it was the first to go mainstream, and very much deserves its success in spite of its various shortcomings
You're completely right, but I think it's worth adding that the O(n^2) to O(n) change isn't specific to React or UI. That same improvement is often seen when migrating other code from a mutating style to a pure-functional style.
"A pure function which transforms the entire input into the entire output" is obviously the simplest possible architecture for many programs, but people hesitate to use that architecture because of performance concerns. In practice, the baseline performance is often faster than they expect, and it can be made much, much faster using strategies like memoisation and fine-grained reactivity.
> "A pure function which transforms the entire input into the entire output" is obviously the simplest possible architecture for many programs, but people hesitate to use that architecture because of performance concerns. In practice, the baseline performance is often faster than they expect, and it can be made much, much faster using strategies like memoisation and fine-grained reactivity.
But before React came along, you just couldn't do this without major UX breaking bugs, because of how the DOM worked.
Say you have a form that you want to change depending on the state of the app. If the user is typing in a form field while an update to the app state comes, and a pure function that transforms (app state -> DOM/HTML output) resets the form (meaning removing the old out of state DOM and replacing it with the new DOM), the user loses focus on the form. So you have to add some kind of logic where the app remembers what form input the user was focused on, where in the field the focus was, etc. The more complex your app is, the more complex the DOM reset logic became, and you cannot abstract your way out of it with pure functions, because the DOM that relies on mutation slowly creeps into your pure functions anyway.
React changed this, because it gives you a pure function interface, but resets the DOM using mutation functions i.e. native DOM methods, surgically. This is achieved with the VDOM (Virtual DOM), by diffing VDOM states and then reflecting that to the actual DOM. This means when the DOM resets, there's no problem with elements getting removed and added back in, and the focus states etc. don't get thrown away with the DOM. Before React, nothing like this existed.
The problem described by antris is that, if a developer were to naively tear down and rebuild the entire DOM tree on each state change, the browser would discard some important state which belongs to the old DOM nodes. This state includes the text selection, keyboard focus, mouse capture, scroll position, and the progress of CSS transitions and animations.
React solves this problem by building a virtual DOM, and then conservatively updating the actual DOM (sometimes just mutating individual HTML attributes!) so that this state is preserved.
React doesn't have this "pure function" feature what-so-ever. hooks are magic stateful functions, not pure functions. They behave differently the second time called and therefore have all kinds of side-effects and restrictions on when, where, and how they can be used.
Accumulative recursive functions also behave differently the second time they are called...
Hooks are a declarative DSL for accumulator arguments to the recursive function (the component).
If you would rather rewrite the render loop in continuation passing style, and have a 46 line recursive call expression which conditionally changes like 15 immutable parameters to set up the next render iteration for your component, I'd like to see the code when you are done.
We've been doing the O(n) thing forever with PHP and CGI since 1995, the issue is that it required constant whole-page reloads and was a poor experience that got worse the more interactive your webapps got. React let you do the same thing but made it fast(ish) with local updates
We actually see similar innovations on the FP side as well, with persistent data structures that allow structural sharing so your "copy and update" operations can be fast enough to be usable (even if still not nearly as fast as in-place mutation)
React isn’t that special. You can do a react-like design in many frameworks, and in fact you can even do it with vanilla web components. The code ends up a bit more verbose and ugly, but it still has that o(n) core design. To give an idea, here’s a video I made where I port react’s tic tac toe example to vanilla web code.
I never want to go back to a non-declarative rendering framework.
Does anyone remember a blog post from maybe 2016-ish which described the process of "building your own" react-like rendering loop? I remember it being a fantastic explanation of what the virtual dom is and does, and why the "UI is a function of state" paradigm is great. I remember it having a live example to the right side, with prose and code on the left. I can't for the life of me find it now :(
To be fair it predates 2016 when the term “immediate mode ui” was “invented”. uis as a function of state were and still are the norm on game development. React introduced this concept on top of “retained mode ui” which is the dom. Never want to go back too despite people spreading so much hate which is not in the library itself I think but on the ecosystem. Maybe react become synonymous of too many companion libraries/bloated frameworks?
Sadly not. I did find this article too while searching for what I remember. It is a close match in so many ways, but also not quite.
I remember the article very much taking the approach that you've never heard of React before, and walking through a series of problems and discoveries like "oh, what if we just render the state to the DOM every frame? let's see how that works".
Almost like a "you could have invented React" kind of vibe. I think there was a button-counter demo app.
Not a live preview and not a button-counter demo at the end, but the overall structure and the whole O(n) vs O(n^2) argumentation was in this blog post from a now defunct domain. Maybe this is what you were looking for:
This definitely looks different, and I don't recall seeing this before. But this is a great post! It communicates some of the very important ideas in a straightforward way. Thanks!
Another one that seems so close that it makes me think I imagined the one I'm looking for, and this is actually what I read. Thanks for trying - it's not causing me to say eureka! but it is close.
The basic idea is if you have n ways your page can display, with the old PHP/CGI whole page refresh model you needed to write n endpoints, but every interaction required a slow and disruptive whole page refresh
With pre-react single page apps, if you had O(n) ways your webpage could display, you needed to write code to account for n^2 ways that each of those n states could transition into another one of those n states
With react, you write code to render n pages, and get the fine grained interactivity for free with good (enough) performance. It basically removed a tradeoff/dilemma that had been facing web developers since time immemorial, so no surprise it was a big hit
I don't know if I'm convinced. I have several react projects stuck in dependency hell. If I upgrade from build in node 18+ (from 16) I start getting errors. If I try to upgrade react then 1 of the 3 external widgets I'm using barfs out. If I do manage to get upgrade I get complaints about all the best practices from react 16 that have been deprecated. If I'd written my own all of these issue would disappear because I wouldn't have made breaking changes to my own framework.
Note: you might say node 16 is old and I agree. I'd much rather be on 22. But since I can't get out of this dependency hell without days or weeks of work the projects have been stuck for years.
Then do it.. you are free to experience it. Especially if it’s just you on the team you might be totally fine.
I will just say that any project that lasts that long will require maintenance work now and then. The issues you describe seem quite minor and are probably relatively easy to patch up. React provides code mods that do a lot of the heavy lifting, your external widgets have updates, or alternatives.
I would also add that you don’t have to use dependencies with react. It’s always good to be mindful of dependencies and limit them.
It’s the same thing, you choose dependencies to save you time, or you do it yourself.
My main point is that with your own framework you will also run into limitations, causing big rewrites taking days/weeks/months, and/or lots of upfront cost..
ScalaRx and Scalatags actually works a lot like React.js, in user-facing experience even if not in implementation. Unfortunately I was just a college kid and couldn't push it the same way the pros at Facebook could push react!
People love complaining about React, but I don't know if they remember the pre-React world where you had to write the code to manage O(n^2) UI state transitions via recursive mutating callbacks. Simple things like "have a the list of photos shown always kept in sync with a label showning count of photos" was incredibly error prone, and the complexity of implementation went up quadratically with the complexity of your app (due to the n^2 state transitions you needed to worry about!)
Today React is not the only one to do this O(^2) to O(n) complexity reduction, but it was the first to go mainstream, and very much deserves its success in spite of its various shortcomings