> article just about to turn three years old
JavaScript community never beating the allegations lol
Basically every framework now has a concept of * State - change this and the framework automatically updates UI and triggers effects * Effects - functions that run when parts of state change (or triggered by signals)
And almost everyone solves this with signals except for React.
At this point, I think React and Lit are the only major frameworks not using signals. And I'm not 100% sure about Lit.
IMHO, React wins because you can just treat templates as variables. You don't need "slots" or other special stuff. It's simply more composable.
The real discussion would be between React's vdom and something like Solid's signals.
It does have its own templating syntax, which is trivial to learn. No more cumbersome to learn than JSX, which is a templating language designed by the React team. Not sure why you chose to make the distinction between JSX and Vue’s DSL as if JSX wasn’t developed for the sole purpose of facilitating React’s virtual DOM.
Vue templates mean learning Vue's custom syntax for if statements, loops, dynamic attribute syntax (with its own gotcha), binding dot modifiers, data binding, and whatever else. It requires learning its entire custom directive system. It uses custom syntax for stuff like events too.
I don't see this as remotely comparable with Vue being much closer to something like my time working with Angular 1 (a time I'd rather not repeat).
Please explain React’s reactive data binding since it’s apparently much simpler than v-model=
;)
The simplicity of JSX is overvalued. It just means more code, and typically uglier code, to achieve the same behaviour as Vue. Which is basically the theme of React when you compare the two libraries.
Vue's DSL is whatever language the developer implemented. Which is probably not enough, depends how much effort they put into it and how good they are in language design. Given that they cargo cult HTML tags to organize components in a pseudo-familiar but not-valid-HTML way, I don't have much confidence in their language design skills.
I'd take the former any day.
{enable && <Form />}
for conditional rendering, or {collection.map(x => <Item x={x} /> }
for looping are not the most obvious choices. If you ask people how conditionals and loops are written in JS, you will get 'if' and 'for' or variants of 'while'. So I get where the v-if and v-for are coming from.The words borrowed from someone else:
The react is more about view.
The vue is more about reactivity
It is a fairly painful process - I am using Codex and Claude as a first pass for converting things, but everything has to be checked manually and often fixed or rolled back to try again. Too often instead of "removing" piece of code that is not necessary anymore, AI would try to add a 100 line workaround for it because it would really rather add things than remove them.
It is, however still a faster process than doing manual rewrite. Smaller components are a breeze to convert from older Vue 2 to Vue 3 composition API.
Rewriting entry point app.ts with bunch of custom plugins was a horrendous experience that eventually was done manually. Move from Vuex to Pinia was also 50/50. Some things were easy, some things were not and had to be done manually.
Then there's a process of switching from unsupported packages which is a whole other can of worms.
Overall - if not for AI I'm not sure I'd have gone with an upgrade at this point. Yes it requires careful review and testing, but a ton of trivial stuff was done very quickly.
Vue just has a very simple lifecycle and combined with a simple store like Pinia it's just really fun to work with.
A lot of these libraries aren't needed in Svelte because 1) the functionality might already be built into Svelte, and 2) you can use any JavaScript library directly, unlike in React where you often need a React-specific wrapper.
Not saying that applies to your specific use case, but I've seen this argument way too many times.
Maybe that's just anecdata, but I hated them a lot less. (I am mostly a Backend dev who also does Frontend, so I don't love any of them.)
[0]: and by that I mean my whole team at the appropriate time, it's not simply me misunderstanding things
Before that was XMLHttpRequest (particularly during my .Net WebForm days) and even had to use the ActiveXObject in IE that predated JSON.
Very happy still with the Backbone way of building webapps.
Compared to previous paradigms, React lets you compose complexity and rich interactivity really, really well. Even for all its flaws.
These days I use web components for component writing and frameworks to handle routing, state management, bundling, and so on.
I migrated from Angular 4 to 18 (including ngrx and material) and didn't find it especially problematic.
Migrating mostly was little effort and consisted of automatic migration and walking through the provided checklist (mostly to ensure I didn't miss anything important), but I don't have any comparison in the JS SPA ecosystem.
> I migrated from Angular 4 to 18 (including ngrx and material) and didn't find it especially problematic.
The pain varies from project to project. Mine has been touched by a generation of developers of different levels of proficiency. A more disciplined project would’ve been easier to migrate in retrospective.
I think Angular ecosystem really missed the train with schematics (even ngrx!). Schematics could tackle a lot of toil if implemented exhaustively but apart from Angular and Material, nobody else seems to implement them effectively for brownfield projects.
Tried React afterwards, this frustration didn't really exist and it was much easier to pick up.
It is apps or pages, which supports what, what new use cases is "use..." now for, ....
Maybe nowadays there is a set of popular libraries for react so it becomes framework-y?
A framework expects most of your project to be shoehorned into it.
Whether something is one or the other depends on which of the two most users are doing. It's not a very interesting argument, though, because debating semantics is the worst use of the limited time you have on this planet.
jQuery isn’t a “framework” either, at least not like Angular or Vue, though it can be extended with “plugins.”
Both jQuery and React are foundational technologies, so comparison is valid.
Both jQuery and React are foundational technologies, and comparison is valid.
A lot of people did and do like that idea — I like it too — but it’s woefully inadequate for making rich web apps that a team of average devs can handle.
The biggest issue when I worked with it was weaving a spiderweb of bindings that eventually trap you. At some point, you wind up spending most of your time fighting weird bugs that show up in places very far removed from the bindings that actually caused the bug.
Plus the ecosystem. It's huge. Nothing comes closer.
It also uses JSX, but since there's no virtual DOM, you can also write 100% JS, but, unlike React, you can do it without any special wrapper. So you don't need to use or write a `react-dnd`, just use any vanilla drag and drop library.
The biggest issue is in finding people to work with it. If you're hiring React developers over web developers, they will probably struggle more with SolidJS's differences from React, in part because they just look so similar that there's more to "unlearn". But most web developer (i.e. anyone who can understand beyond just the confines of their favourite framework) should find it relatively easy to understand what's going on.
I guess technically that part of the application didn't scale so well in SolidJS, but it scaled a lot better than it would have in any other framework, and we got to delay writing the engine "properly" until we'd already built everything else and got it running with real users in production.
Same goes for component libraries.
How did the React community convince so many people of this falsehood? Do that many people just not know what javascript is? It baffles me that one could look at JSX and be like, “that right there is vanilla javascript”.
> React has its tradeoffs, but we got here after a long slog of other things that don't work.
I strongly believe it's because of trying to achieve the wrong goal with the wrong tool. So many websites could just be bare html pages and forms with just a sprinkle of JS for some interactivity, but they want to add JS for whatever reason.
If you can have a complete repo browser without JS (cgit), most web applications can survive without it too.
First is the pursuit of polish. Each extra 1% in polish adds tons and tons of lines of code. If you want that level of polish on a non-SPA, you'll still have to add all that code then reload it one page at a time. I see a lot of these "bare HTML pages" and they are lacking important stuff like i18n/a11y/WCAG compliance. Try adding all that back in and you'll see your website bloat right up.
Second is bloated do-everything libraries. Ant, MUI, Mantine, or whatever else is aimed to be a superset of all possible website needs which means that the components you adopt have tons of features and bloat you don't need that slow down loading, parsing, and execution. Simply replacing that <Paper> component with a <div> and a few lines of CSS will get you the same thing you want, but will save you layers of unnecessary React components and sometimes a layer or two of unneeded DOM nodes as well that were added because the <Paper> component had weird interactions with some other component.
Third is manpower/experience. Many/most JS devs today (sad to say) don't actually know how to make that simple <Paper> component on their own. Those that do often skip it because they've got too much to do already. I've lost count of the number of teams I've seen where a bog-standard backend has 25 people working on stuff while the frontend team has 3x as many total lines of code (which are often times handling human-computer interaction issues the backend couldn't even imagin), but only 3-4 people to maintain it all.
Fourth is of course management. Designs on the backend change at a trickle while changes to the frontend arrive in a torrent. Understaffed frontend teams can't keep up with all the things shoved on their plate, so they usually can't optimize things even if they know how (eg, only a small percentage of SPA actually know/take the time to lazy load various parts of their apps to improve load time).
Fix these things and the SPA performance will improve drastically and almost certainly exceed BE templates with some jQuery spaghetti.
having lots of JavaScript tends to make more WCAG problems, because you do interactive stuff that needs to be described. Having bare HTML and the accessibility that is required for that is not tending to bloat in my experience.
When you try to chain that stuff across multiple backend-rendered pages, you get a whole other list of problems. If you need to track all the otherwise transient UI stuff on the BE, you have now created a whole mess of stateful APIs and turned horizontal scaling into a much bigger issue than it needed to be.
so you argued that the need for internationalization and Accessibility was such that if you tried to add that in to bare html - css solution that you would end up getting bloated, I argued (based on my experience) that accessibility bloat actually comes from the need to support various JavaScript bits of accessibility.
To which you then argue that the JavaScript parts are needed.
If so it is the JavaSript that adds the bloat, because the accessibility bloats to support the JavaSript.
I mean I'm not against JavaSript but I have seen many sites that didn't need it; and I've been forced by work requirements to add in too much of it on sites that needed very little or even none at all.
> If you need to track all the otherwise transient UI stuff on the BE
So don’t add transient stuff? You send a page or a form according to the URL, the user does whatever it wants and either request a new page or submit the form. Each page is standalone like activities in an Android app. Anything transient is taken care of on the client side.
Web components are the next big idea. I hope they have a chance to work.
This comment could work equally well in any of the past 15 years. And for all we know, any of the next 15.
It is mostly the React crowd that dislikes them.
It is also somewhat ironic that until late 2010s a common complaint about web development is how fast it changes and how many new things are coming up all the times. It was a very valid complaint, of course. But then when the React monoculture rose to the top, and everyone decides to complain about how that sucks instead. You really can't win.
React wins because it has become a default choice and folks like what’s comfortable to their preferences
I see a lot of personal projects, solo founder applications etc running made in React. I respect your opinion but other people definitely do choose it when they have fully control of what they use.
Secondly, when someone new asks me what web framework to learn, I tell them React. And the main reason, every time, is that this is more likely to get them a job, i.e., inertia.
We got here somehow. Clearly, React didn't get here purely on momentum, and must have done something less poorly than the other frameworks. But I think it's hard to deny that inertia plays a big role in its current popularity.
I used to be on a team that used Mongo for a relational database because Mongo was trendy at one time. So all joins had to be done in the application layer.
If I want anything else, I have to implement the integrations myself, search for some open source project that has already done it, or ask AI.
Doable on hobby projects, unthinkable in professional settings.
I wanted to make a back button use browser APIs to go back if the coming from the inbox, just link to the inbox otherwise to preserve scrolling. I had to wire the actions from the html to call the function that goes back, then in my controller determine the previous page and send the JS enabled back button or the hard link. My logic was spread out over 3 files!
With React I can have js in a component determine if the previous page was inbox, and based on that value show the back button JSX or the link. ALL IN ONE FILE. One conceptually entity for me to model vs 3 that do other things and this functionally is hammered in.
Is it slower? Definitely. But it makes me happy. Miserable in a corporate React slopbase? Blame your coworkers, it would definitely be worse without it.
This is why I hate react spas. They're always trying to find some stupid way to break my browsers back button and navigation buttons.
I will always prefer htmx/server rendering with native everything (except the occasional form boosting.)
The problem with just calling history.back() with no fallback is it will bounce users out of your app (back to Google or wherever they came from) and PMs won’t like that…
For example, if you are on Page 5, then pressing "back" inside the app should always take you to Page 4. `history.back()` could take you to any page, it's unpredictable.
And the idea of logical navigation is flawed because most websites don't have a well defined logical structure, nor is it feasible to have one. What is the previous page of a Wikipedia article, or an HN submission, or an Amazon listing, or a search result of cheapest direct flights between New York and Cancun? With how the back button currently works, at least there is consistency in what to expect when clicking it. Under your suggestion, there is no way a user knows what the back button does on each website unless he clicks on it first and find out himself.
For example on instagram you might click through to a post from the explore page or from someone sending it to you via DM. In either case pressing the back button rendered in the app, or swiping back, will take you back to where you came from. It feels natural and seamless. Although I guess there are other ways to skin that cat than history.back()
But I agree with you when there’s a clear hierarchy. Like on a job ad a “back” button should just be a normal link to the index of job openings.
I think that lots of more traditional websites have very poor back button designs, especially around editing and form submissions. Remember clicking back and the browser prompting for form resubmission? Very poor design since you have no clue how the server will even handle form submissions. Or getting stuck deep in an application, hard to get back to the root. Or, consider encoding current page data that you’re editing into the URL, and back buttons don’t return to root and just strip query params. Often a very frustrating experience.
Often, “go back to what I was doing before” is what I actually want, not “go strictly to the previous state in the URL bar.”
Sure, plenty of people mess that up too, but the reality is that controlling the navigation stack can help you build more useful designs.
You hate BAD react SPAs that break the fundamentals of how the web works. Good ones take care to not do that.
React fundamentally doesn't cause this issue either. You can use a different framework than react or even vanilla JS and still produce the same bugs.
But that’s all of them? If Github, Reddit, LinkedIn and Facebook and others are unable to build SPAs that don’t constantly break the fundamentals while also choking the browser, maybe it is a tech problem.
The recommended htmx way would be to hook up an onclick button to inline js or if you dislike that, a function called goBackOrInbox. It can then be something like:
function goBackOrInbox() { if (document.referrer) { const path = new URL(document.referrer).pathname; if (path.startsWith('/inbox')) { history.back(); return; } } window.location.href = '/inbox'; }
And if you use that pattern a lot then you can parameterise the function with whatever the route should be.
The problem is that you cannot introspect the browser’s history with the history API. So you have to hack your way around that if you want the “go back in history if possible, otherwise navigate to fallback url” behavior. Which I guess is easier if you’re in a react SPA. Or if you’re fully a MPA and can just check document.referrer
There’s a brand new Navigation API that does let you introspect history entries from the same origin, which perfectly addresses the issue.
I wrote a polyfill in order to take advantage of the navigation API for this exact problem: https://github.com/kcrwfrd/navigation-ponyfill
Exactly.
I did a somewhat longer writeup a while back.
https://blog.metaobject.com/2018/12/uis-are-not-pure-functio...
The pull request is still open :-)
Narrowmindedly worrying about syntactical differences is contributing nothing to the conversation. The point is relinquishing control of state to the framework (be it via props, hooks or @State), and drawing the UI as a pure representation of whatever the framework tells you. Hence ui = f(state). This gets you a metric ton of advantages, which is why every modern framework is doing it.
Classes, by themselves, are inadequate as containers for state unless that state must only exist in memory and never be observed, synchronized or serialized. You can attempt to patch that with decorators, ORMs or whatever, but now you're doing the same thing as React is doing with functions.
Why do you think it would?
Did you misread my post as "ha ha, Apple does it differently and therefore react is wrong"?
If so, you certainly misread it a lot, and you have the wrong person. I have tons of posts about Apple getting things wrong.
Anyway, did you miss the following part?
I also think that despite all the flaws, react.js and react.native are currently eating native development's lunch.
Clearly I made the point in my post that this approach has a lot of mindspace, despite the obvious flaws, so not sure how you pointing out that this approach has a lot of mindspace invalidates my post.
> Hence ui = f(state).
Every UI is always some mapping of the state. Otherwise it isn't really a UI, is it? What would it be, in your opinion, if it didn't map the state?
> pure representation
What does "pure representation" mean to you? I covered in the post that it sure as hell isn't a pure function of the state. And cannot be.
Did you miss the part where David Abramov conceded that point?
To elaborate a bit, React components aren’t always “pure” in strict FP sense of the word. They’re also not always functions (although we’re adding a stateful function API as a preferred alternative to classes soon). Support for local state and side effects is absolutely a core feature of React components and not something we avoid for “purity”.
> This [ui=f(state)] gets you a metric ton of advantages,
Well, yes. Like being a working UI, for example, which is why MVC is also structured this way.
But can you elaborate the specific advantages you believe the react approach gives us over, say, MVC?
The post elaborates that it would be really nice if UI were a pure function of the state. But it just isn't. And trying to pretend that it is might seemingly buy you some of those advantages, but at a huge cost.
> Every UI is always some mapping of the state.
Sure, but it previously wasn't described as such. It was described as a list of imperative operations "if x do that" (unless the developer just gave up and had a giant refreshEverything() function). IIRC, at the time, MVC had a common problem of controller bloat, precisely because it was trying to perform the reconciliation between view and model imperatively rather than simply describing it as a function. MVVM solved this partially with data-binding, but depending on the framework, you still had lackluster handling of derived state inside the view-model.
> What does "pure representation" mean to you?
I mean that, the render function of the component is a 100% pure function of the state that the framework injects. By "render function" I mean everything except the statements beginning with `use...`. Those are just React's syntax for defining the component. Another framework such as SwiftUI might have defined them outside the function.
> But can you elaborate the specific advantages you believe the react approach gives us over, say, MVC?
There are so many resources on this, so I don't know what I can say to convince you, but I'll try:
* The Compiler. Regardless of whether you think it’s self-inflicted complexity (I don’t), functional patterns generally are easier to statically analyze and transform.
* Transitions, concurrent rendering, suspense, & time-slicing. All of these are possible by rendering components with outdated state. A low priority update may trigger a loading state and defer its commit. Meanwhile, high priority updates can continue to work as usual. In the past ~2 years, this is easily where I've seen the most UX & DX gains in frontend.
* Most importantly: readability. Even if components are only 90% pure, I still see side-effects and state clearly marked. Even in MVVM, I've seen so much spaghetti from my coworkers. In React & post-React frameworks, when debugging a component, I have a much narrower range of things I need to check. For example, I don't have to worry that an object I'm reading from is being mutated by a component on a completely different route.
Overall, the reason why this post rubs me the wrong way, is that you took a theoretical design document, misunderstood it for the actual, practical implementation and snarked at the authors for not going with the "obviously correct" solution of OOP. Not seeing the bigger picture in 2018 is understandable, but now it's... interesting.
What makes you think I trust Apple? So yes, that's very much a "you thing".
And SwiftUI is laughably bad. And my very early criticisms of Swift, for example, have panned out precisely as I predicted.
> > Every UI is always some mapping of the state.
> but it previously wasn't described as such.
Yes it was. Except this was seen as the obvious given that it is. Because, once again, a UI that is not a function (mapping) of the model is not a UI.
"In Smalltalk-80, a view is just a visual representation of a model" -- https://en.wikipedia.org/wiki/Model–view–controller#View
> IIRC, at the time, MVC had a common problem of controller bloat,
Nope. Non-MVC implementations had the problem of controller bloat, because they didn't actually implement MVC.
> the render function of the component is a 100% pure function of the state that the framework injects.
Except, as I've correctly pointed out: it's not.
[Advantages]
None of this is in any way specific ("The compiler". Seriously?) or clearly an advantage that can be tied to this type of framework. So yes: you're not even close to convincing me. Because there's no "there" there.
> the reason why this post rubs me the wrong way, is that you took a theoretical design document, misunderstood it for the actual, practical implementation
You misunderstood the post. Completely. The document claims "pure function". This is laughably false. The question is what is left when you remove "pure" from "pure function". And the answer to that is: nothing.
That doesn't mean there are no benefits, but the benefits cannot be "what you get from being a 100% pure function", because it ain't.
Just like the benefit of butter can't be "it's 100% fat free". Because it ain't.
And if you keep insisting that those are the benefits, then I don't know how to help you.
And again, it doesn't mean there are no benefits, but they are both smaller than and quite different from what you claim. And with the benefit being fairly small, the other question is what the cost is of pretending this is so when it is not. And the answer is: pretty high.
And it's funny that on the one hand you go with the same "well, you're taking the "100% pure" thing too literally", when just a few lines above, you yourself made "100% pure function" the defining characteristic.
So which is it? Make up your mind. It appears to be Schröding-important.
Any UI that actually is a UI is some sort of function of the state. Otherwise it is not a UI but random graphics and/or decoration.
Even though I'm a fan of React, and use it for practically every web application I build, my biggest and most obvious issue has been that writing UIs through React doesn't feel as natural as, say, writing command line tools in Go, or live/realtime apps in Elixir.
Some languages just feel incredibly natural and frictionless for certain things, and nobody has really nailed UIs yet. Swift, JSX/HTML, Svelte, or whatever framework of the week: they all feel like they're working around the problem to some extent. Like at some point in the process, the designers of the language/framework had to compromise and implement some hacky/weird/painful syntax to satisfy project requirements.
UI's natural interface is visual, so tools like Figma can serve as an essential part of the solution, but nonetheless, I feel there's something missing. There must be a more intuitive way to represent the visual through code. The current solutions, although I find it hard to describe precisely, are always tantalizingly lacking in one way or another.
The most reliable and performant UI code I’ve written usually tackles this head on by thinking through all the possibilities of different states the application can be in and how the transition of each state should look. This can very quickly get mind boggling complex.
Systems programming languages seem like they model their problems better because it’s less common to ask a systems programmer for a like button that shows bubbles when you press it. If you model your UI explicitly like a state machine, you can say “no” to the bubbles like every systems programmer says “no” to most features, because you realize the explosion of state will be unmanageable. Or you can use a JavaScript framework and download a bubbles package and wonder why your app is buggy, but have job security.
I still prefer it over almost everything, including Svelte, Vue and Solid. But I have started using Crank (https://crank.js.org/) which seems a step closer to where I want to end up. However, I have so far only used it for toy projects so I can't speak to how well it will scale, both in terms of performance and DX.
Emphatically yes. If you look at books written about the problem in the early 90s[1], they are still applicable today.
> The current solutions, although I find it hard to describe precisely, are always tantalizingly lacking in one way or another.
The best analysis of this I have seen so far is in Chatty's Programs = Data + Algorithms + Architecture: consequences for interactive software engineering [2]. It's a bit hard to get through, but absolutely worth it.
As a short summary, the problem is architectural, or more specifically linguistic/architectural mismatch: the architecture our "general purpose" programming languages induces, which is the call/return architectural style, does not match the architecture required for user interfaces, but rather conflicts with that style.
I also wrote about it in Can Programmers Escape the Gentle Tyranny of call/return?.
My current approach is to first build a programming language that can easily express alternative architectural styles: Objective-Smalltalk [4].
With that I am now working an a UI framework I call interscript, including HTMXNative and other goodies.
It seems to be working out...
[1] For example, Languages for developing user interfaces by Myers et al https://api.taylorfrancis.com/content/books/mono/download?id...
[2] https://opendl.ifip-tc6.org/db/conf/ehci/ehci2007/Chatty07.p...
[3] https://2020.programming-conference.org/details/salon-2020-p...
Yet, as the years go by, I find myself accepting a less idealistic answer: Maybe there isn't. Maybe the problem space is just so complex that no one (humanly feasible) general solution exists for all forms of it. If there's one thing that this is true for, UI is probably it.
The solution might be well out of grasp now, but think 10, 20, 100 years from now. Somebody smart with a new perspective will eventually come around and tear out the roots, and we'll all be better for it. Perhaps what's holding us back isn't the problem itself, but the way we approach it and the assumptions we bring.
Trying is at least worth the effort, especially since "better" is very achievable, rather than total perfection.
If you ask the people who work on any of those frameworks I mentioned, they'll tell you they're taking the React style and applying it to their platforms.
GOTO is also pretty intuitive.
They try to give you the vocabulary to be expressive, then get the hell out of your way. You can still shoot yourself in the foot with them, of course.
I think that has more to do with the design goals of the language. C was designed to be raw and simple, and it does that well. Ruby was designed to read almost like English prose, and it does that well. Rust was specifically designed to make it hard to shoot yourself in the foot, and it does that well too.
I don’t think that was ever a major React design goal. PropTypes helped a bit, but these days TypeScript carries most of the burden of saving developers from themselves.
Anyway, React is far from perfect. But JSX was a neat invention that alleviated the tension between
`<div>something</div>` and
let el = document.createElement(‘div’);
el.innerHTML(‘something’);Unfortunately, React's biggest problem is that it forces you into the JS/TS ecosystem, which is, without a doubt in my mind, a compilation target rather than a system I wish to interact with natively.
I'm happy with Elm -- the community is really small, and sometimes you have to roll your own libraries. TEA is sometimes... unnatural (coming from React), but the fact that you do not have to worry about implicit and unexpected state (see useEffect), I always get excited to work with Elm.
Additionally, Claude seems to manage itself better in Elm than in React, at least within large, scary codebases.
Totally recognize and agree with the lack of libraries -- that's really Elm's weakest side.
>how it scales for apps that may have reusable components or sufficiently complex
It is a lot of boilerplate, but it is mechanical, straightforward changes. I think it is entirely possible to automate it (not even using LLM).
> state..
I have used ELM ports to interact with JS and localstorage/indexeddb.
For example, https://harmont.dev's app (landing page is not Elm) has a component to render a pipeline graph called DagGraph. In React, you'd do something like
function DagGraph() {
const steps = useState<Jobs[]>([]);
const selectedJobIdx = useState<number | null>(null);
return ...;
}
In elm, I lift this up (I will keep using React syntax for the sake of wider-audience readability) function DagGraph({ steps, selectedJobIdx } : IDagGraphModel) {
return ...;
}
This allows for better testability of the graph view. The parent then injects the model function PipelinePage({ graphMode, selectedPipeline }: IPipelineModel) {
return (
graphMode === GraphMode.DagView
? <DagGraph steps={selectedPipeline.steps} selectedJobIdx={selectedPipeline.selectedStep} />
: <TableView steps={selectedPipeline.steps} selectedJobIdx={selectedPipeline.selectedStep} />
);
}
Now -- we need to bubble up the selected pipeline index based on when the user clicks a node in the DAG view. I personally prefer to do this completely orthogonally to the view rendering pipeline (which remains mostly pure). I create a new model, and define the message relationships to it: namespace PipelineState {
interface Model {
steps: Step[];
selectedStep: number | null;
};
interface ISelectStepMessage { stepIdx: number };
interface IUnselectStepMessage;
type Message = ISelectStepMessage | IUnselectStepMessage;
function update(m: Model, msg: Message): Model { return...; }
}
// main model
interface Model {
pipeline: PipelineState.Model,
}
type Message = PipelineState.Message | SomeOtherMessage | ...;
function update(m: Model, msg: Message): Model {
switch (msg) {
case PipelineState.Message as msg:
return update(m, msg);
...
}
}
And this seems to work quite well. Personally, I really like this architecture, because it enables me to think about state and UI separately. One of the really big gripes I have with React is how most components end up with `useEffect` and `useState`, and then immediately become untestable. Elm literally doesn't allow for that.At the end of the day, there's a trade-off -- my logic is no longer localized to the "component" relevant to rendering the pipeline DAG. I never have one file open editing Elm code, but I'm a vim user so it doesn't bother me. In some ways, semantically, I think it makes sense to split the "state backend" of your UI from your UI, in my opinion.
Most of my app states are represented as state machines (coming from embedded this is quite natural) and UI is represented as pure transformations of that state.
With Elm, it really feels like you reason about state and UI separately, and I actually prefer that, both for testability and that's just how my brain works.
I've found it to be a great improvement over many cases where I would have used React before.
Svelte feels much easier to learn for someone who already knows the basics of web development, HTML, CSS, and JavaScript. But nowadays I often see people start learning web development by learning React, which feels a bit backwards.
Personally I have been using SvelteKit + Capacitor to build mobile apps, wrote about the setup here: https://bryanhogan.com/blog/web-to-app-sveltekit-capacitor
For simple landing pages I like to use Astro though.
> But nowadays I often see people start learning web development by learning React, which feels a bit backwards.
I think Svelte prevents this nicely by treating HTML as the mother language. If someone started web dev with Svelte(Kit), they would probably learn more about the fundamentals than they would with React.
I like ELM because the core is extremely small. You just have to remember the model, the update function and the message type. That is it.
React on the other hand makes you remember a million conventions and patterns and api like useEffect, useState, hooks.
After I was laid off I realized that what I actually loved about react was JSX components, so I wrote a template engine on top of JSX and started doing server side rendering in my own apps. It's a quiet life
You love JSX, you don't love React
Many of the jobs in my location requires React though, so I have to tolerate it somehow.For what it is worth, I'm rather proud of my smaller reactive (RxJS-based) JSX alternative (Butterfloat), which is not a Virtual DOM like React or Cycle.JS (it is more "What if Knockout but TSX?"), and I am working on its second version now (among other things expanding its SSR/SSG powers, which already have a very different, I'd argue simpler, take on SSR/SSG than the Next.js/Nuxt.js/React Server Components world).
That's neat, will give it a spin in the future.
I have heard a little about ClojureScript here and there. Will take a look at it when I am free!
My favourite front-end architecture is MPA actually mostly server-rendered, with Vue only on the pages that need high interactivity, and vanilla JS on the others.
I have never found the idea of having a Virtual DOM and diffing in runtime a good solution to the problem, maybe that's why I never liked React. I mean if you are writing a lot of code, have an enormous build step already and use a bloated library, why not have it compiled too anyway. That's why I like the thinking behind Svelte.
I personally like JSX quite a lot. Solid.js is a framework which uses JSX but drops the virtual DOM. Its creator has a course 'Reactivity with SolidJS' on Frontend masters. He's a compiler enthusiast and tells you quite a bit about how much he had to learn from the React project, which, to me, it put into perspective the kind of thinking React brought to frontend. I won't code in React myself, but I surely appreciate its massive influence on everything else.
Ultimately I think React makes it too hard for the performant solution to be used. And then tries to handwave it all away with “the react compiler solves/will solve it”. Don’t even get me started on “useMemo is not semantics” rationalization. First time runs actually matter when working on a performant UI!
DOM ops are expensive but your little bespoke function component code is also expensive when some hook leads to recalcs all over.
Granted none of these critics have a viable tried and true alternative. It’s easy to throw shade at any abstraction. None will ever be perfect!
That said, I welcome any new paradigms and ideas. But discounting how far we’ve come with what’s possible on the frontend with hand waves just doesn’t cut it for me.
Sure these companies have influence, but it’s not all or nothing.
I just don’t buy the whole mass hysteria and funding being responsible for reacts prevalence.
“Frontend purity” reeks of the tired, “lazy unskilled engineers should write software in C and assembly on 16kb memory like the old days”
Reacts success is not from being an air tight optimized and perfect abstraction, it succeeds because it has a scalable abstractions which have relatively good ROI when you learn it even with its warts.
React has had Facebook promoting it for over a decade… it's hardly a marketplace of ideas… more "Facebook is using this so it must be good enough for us"
1. Over-reliance on complex concepts and terminology. Let's compare with Vue: `useEffect` vs `watch`, `useMemo` vs `computed`.
2. This useless "clever" stuff also slips beyond terminology: many years ago, Redux was considered the go-to state manager, and it required writing a lot of code across multiple files even for incrementing a number, because its author liked a lot of clever CS concepts. VueX at the same time allowed just to increment that number. Thankfully, nowadays, the React ecosystem is full of sane state managers.
3. React ships without any tools to work with CSS. That said there is no chance you would use React without CSS.
4. React doesn't bother optimizing anything for you. You need to know how and when to properly use or not use these clever `useEffect` and `useMemo`. There is a lot of stuff to know, and there are a lot of folk legends about optimizing React. This is while rerendering is its main purpose. In Vue, the framework makes you always use its tools, and does most of the optimizations inside them. I never thought about manually optimizing a Vue app.
5. The folk legends. React API and the "correct way" to write React radically changed so many times, so it is very hard to understand what is still true today and what is not.
All of that can be summarized into one – React is overly focused on ideas, computer science, and being high-level. And not very focused on actually making drawing interactive HTML on screen simple.
I do write a lot of React, Vue, and Svelte. And when I write React I always need to think about stuff, of which I never would think about with Vue and Svelte, because they already took care about it. And performance wise they are on par.
btw, I wrote a related post some time ago https://www.brachkow.com/notes/what-i-like-in-vue/
1. JS supports JSX literals so
let elDef = <div id="some">Text</div>;
will be compiled into let elDef = ["div", {id:"some"}, ["Text"]];
2. We extend DOM API to accept such constructs: element.append(elDef); // same thing as element.append("<div id=some>Text</div>");
element.prepend(elDef); // ditto
element.patch(elDef); // patch element's DOM by elDef
3. Add appropriate events: componentDidMount, componentWillUnmount, etc. for cases when tag in JSX (uppercased) resolves to a class or function.4. Add render() support. A method that generates tree of elDef's. It gets called by append(),prepend() and patch().
And we will get native React implementation. This will be quite useful and allowed to marry React alike approach with WebComponents into single mechanism.
1...4 is how it is implemented in my Sciter as the Reactor thing, see: https://docs.sciter.com/docs/Reactor/
let ele = <div id="some">Text</div> // ele is now HTMLDivElement
Doing that `element.append(ele)` just works with existing DOM APIs.Instead of supporting classes and functions in browser-supported JSX with their own life cycle model, you can just require registered Web Components and reuse existing Web Component life cycles.
I think if Browser-native JSX support were to happen it should probably either be real DOM or remain a function tree so that at least some tree operations can be amortized by the function compiler.
Which is that most JSX is actually compiled to nested calls like:
let elDef = jsx("div", {id:"some"}, "Text", jsx("div", {id: "inner"}, "Text2"), ...otherChildren)
The `jsx` function can potentially avoid some future tree walks/recursion by the nature of being unrolled by the compiler into nested calls instead. That's a useful property lost in trying to agree on a single virtual tree definition. It's also one maybe unnecessary if just relying on real DOM trees, because browsers have their own optimizations for DOM tree walking. Ask HN: "Does Anybody Actually Like HN Engagement Bait"?Currently I'm using hono/jsx, mostly on the server, which seems like an even simpler way to do it than Preact. The JSX looks pretty much the same.
My problems with React are more about JS/TS and how other devs (it's never me! I swear!) can write terrible code that passes tests and isn't readable or maintainable. The JS Tradeoff is that we can handle rapidly evolving web landscape, but we have to deal with NPM and supply-chain hacks periodically. shrug
I am fine with the complexities of events, hooks and re-renders in React because we want to build interactive programs and nothing comes for free. Data fetching libraries like Tanstack Query solve one of the hardest problems of cache invalidation/re-rendering.
React was the thing that made me actually enjoy making interfaces for users. I just want to build stuff.
I experiment a bit with HTMX for my super simple single-page tools with some basic javascript, Next.js is nice but kind of thirsty to get you to use Vercel, I like Flask-Admin for really simple tools that must render a page from a server-hosted application.
HTML, CSS and a scripting language inly for progressive enhancement are such beautiful, pure ideas, and learning frontend at the height of the web standards movement made me a partisan of using these technologies as intended. But these days, doing it that way feels like building a house with Japanese joinery techniques.
But having built websites and apps since before jQuery times, I strongly disagree with manual DOM manipulation as an alternative. Declarative, component-based approach won for a reason. These frameworks allow you to tell how the UI should look based on the state and manages the transition (either via virtual DOM diffing or fine-grained reactivity) for you. DOM manipulation requires you to write some code for every single state transition (instead of every _state_). And, in practice, it becomes unmanageable very fast, and you give up, and start writing code like `element.innerHTML = ...`, causing problems with focus and event management. It's fine for small widgets, even enjoyable, but only until you need to manage a complex UI. Then, you end up with a mess of code that is hard to maintain and debug.
I know some still feel web is not the right platform for building complex UIs but that battle was lost more than two decades ago. Web is good. It comes with accessibility features (like zooming) and works everywhere. As someone with age-related farsightedness, I hate native apps that don't allow me to zoom in with a passion (which is, almost all of them). Of course I hate websites that don't allow me to zoom even more because they had to go out of their way to disable a basic browser feature. But getting decent accessibility is harder with native apps. You basically have to build everything yourself. Web gets you 90% there for free.
React might not be the best out there and might be, one day, replaced by one of the competitors or something new. But declarative, component-based UI development is not going anywhere.
RSC isn’t React.
> Next.js 15.1+ is unusable outside of Vercel
Next.js isn’t React.
By contrast to nextjs, React + Vite is quite a nice combo. Maybe Bun or Deno are also good? But nextjs and RSC should be kept separate from the discussion.
With React I find there is usually a clear and simple way to achieve what you want, and while it doesn’t perform super well, my customers get more value from a maintainable codebase than a fast one - because I can add features faster.
But it seems becoming. Many React maintainers are on Vercel's payroll and Next.js is also defining where React is going.
Like many, I came from pre-SPA days -> AngularJs -> Angular 2 -> Modern Angular. I tried react a few times, plus React Native but found it hard. I wasn't good with a slightly different render-syntax (whereas Angular uses HTML) and every project I was on was wildly different from each other.
Modern Angular has been wonderful. A lot of the pain points people have are gone, some good parts of other frameworks (signals, single-components vs modules, etc) have been incorporated and embraced.
> (whereas Angular uses HTML)
Angular has as complex a template language as any other major framework. The fact that it uses .html files is a convenient lie to make it seem like it is "just HTML". Its template compiler is a far more complex beast than JSX. (And it doesn't have anything like half the type safety of TSX.)
i actually started with angular 1 before it became popular. i evaluated the alternatives at the time, knockout, ember and a few others, and angularjs just looked the best. finally when angular 1 was no longer maintained i discovered aurelia, in part because rob eisenberg had an interesting story about how he got invited to the angular team because of his ideas, and how he left again because he could not actually get his ideas implemented.
it's a bit disappointing that development of aurelia 2 is slow going, but in some ways i consider that a good thing, considering how fast vue and svelte are evolving away from what they were originally.
i need to look at solid.js though. the only promising newcomer in resent years if the state of js survey is any indication.
For me, coding is feeling intuitive as a human being even when I am writing code for the computers (but also for other human beings who'll read and work on my code; not sure how much would that be post-LLM world but still...). React never felt intuitive or, say, natural to me. It "feels" upside down to me, a bit anachronistic (in some way). But as I have seen with many frameworks, or rather "paradigms", which become fashion in the end, "the tools in vogue" because that's what the largest population of coders use, this is what one usually has to use now. I was quite sad when I saw Jetpack Compose as an Android dev. Technically it had improvements over the XMLs no doubt but then seeing it was React was quite not great (at least not for me). But this is what it and one just deals with.
I wish the coding world wasn't obsessed with patterns, architectures, and the need to fit everything into something concretely established (or in vogue). I often see Frankensteins as results.
> How to build a counter component using the HTML Framework in just 1 line of code
> Locate your /node_modules folder and drag it to the trash bin.
> Scott Jehl
> 5th October 2024But now I just use it to get paid cause it's the standard and I know it well. In my experience keeping a large React codebase simple requires some skills that are clearly not universal.
If I have the choice I use Svelte because reactivity is much easier with it and it includes most of what I need to focus on building things.
Back then the question we were looking at was whether it would be good idea to move away from SAP UI5. The alternatives back then where React, Angular and Vue.
The conclusion we came to was that it was definitely worth to migrate, but to what was not so easy to agree on.
Right now I am working with a legacy Java codebase that was based on RxJava. And every single day I am cursing the people that made that decision. It seems so obviously a bad idea. And the only thing that lets me keep my sanity is remembering that every decision only becomes obvious with hindsight.
So I guess the only thing I can contribute is that it could always be worse and sometimes making the bold and seemingly innovative decision comes back many years later to bite other people.
But as it turns out it's a great abstraction worth using for the right things (not every part of the web) and one of those are Single Page Applications.
A lot of comments here are about people linking JSX instead of React and that's a good abstraction too. In Mint (https://mint-lang.com/) I'm trying to create a language for SPAs and having HTML syntax helps.
I have no idea why people go for these super heavy frameworks. Maybe there are some aspects I am not considering that justifies React.
It’s hard to explain, but the way everything was encapsulated as a component just clicked for me and suddenly the whole app could be composed.
I think it was when they introduced functional components. And when it comes to hooks, I love them.
Not to say other frameworks didn’t do the same, there was something special about React.
Later I've a managed state, cause let's agree, it's pretty convenient to have UI updated after a change in the state.
(1) The extreme boom of software engineering as a "get rich quick" career over the past 15 years, and it being the "default" framework for doing stuff on the web. It's so bad, in fact, that most developers these days don't even really understand the difference between a backend and a front-end. I've had to explain, from first principles, how cookies work. All these very important details are simplified or straight up buried by React and its ecosystem.
(2) The overall groupthink of engineers: a lot of us will weirdly become fixated on some framework, operating system, programming language and turn into absolute zealots. This has a long and storied history (Linux vs Windows, C++ vs Java, and so on, so it's nothing new). React just happened to capture a lot of the zeitgeist even though it was objectively the wrong tool to use for like 90% of use cases.
(3) Terrible alternatives. I mostly blame the W3C for this, as JQuery helpers (selectors, AJAX/websockets, etc.) should've been inducted in the DOM standard much earlier and because the W3C (and by extention, the ECMAscript committee) is essentially a beaureaucratic battleground for big tech[1], it's painfully slow to get anything passed, standards are all over the place, and everyone tries to push their own agenda (Google wants to track you, Facebook wants social stuff, Apple wants secure payments via fingerprints, etc.)
(4) The startup boom of the last 15 years or so. This has always been a bit of a problem, but a common trope has been (and still very much is): if it's good enough for "huge tech company," it's good enough for us. So you've had a ton of startups that have been built from the ground up on React and the sunken cost has always been too much to switch.
[1] https://www.theregister.com/software/2018/04/13/go-away-kid-...
I think we should return back to that with the era of LLMs. We don't write the code anyway. Let's use reducers and fully event sourced states.
It seems … okay? I feel like I can reason about it. But I worry I’m missing something that’s going to come back and bite us later because we haven’t adopted a framework.
Roast my stack?
If I had to judge it based on this alone post alone:
Great, seems like a straight forward stack. Would happily work on it.
Only criticism, to get all the ergonomics out of alpine, you sometimes have to lax some CSP or use the CSP build. Not necessarily a deal breaker, it is what it is.
Will probably become a hassle when going planet-scale (tm), but I think for most use cases this should be fine.
2. if one is too much then use Svelte to make 1. Svelte works perfectly okay with other JS libraries without needing wrappers.
3. use Vue if things get political(i.e we need devs) if you need to play around but u can also use Vue to make 1.
1000xxx. if you tokens to burn & VC money to burn the use React - since your decisions are influenced by politics of the day and not pleasing your users.
(Purely based on vibes, I do not have anything robust to back this up.)
> A cherry-picked collection of React (and React-tainted) criticism.
I appreciate the honesty up front!It also doesn't help that every single react codebase will always be drastically different from one another. In fact the easiest way to know the date of a react project are the dependencies one chooses in package.json. IDK if that's good or bad, but when you see bootstrap + sass in ${current_year} it's not going to be a good time. Compare this to something like Go where most projects are quite similar. Never had issues jumping in completely new Go repos but react projects are always a massive gamble toward always sucking.
No solutions from me, I charge a premium working on react and there is no shortage of clients with garbage react projects needing help but can't imagine the waste of pure human effort put into maintaining such projects to begin with.
State library is prob all you need now.
Hopefully if the signals standard proposal (TC39) gets up we may not even need state libraries soon.
Now if they could just make Typescript semantics native.
But React has the largest ecosystem
It didn't click until I saw a react+graphql project, and it makes sense why meta created react and graphql
It is gotten weird after Vercel's take over development, with all the "use whatever", which is getting out of hand.
Also the whole functional spaghetti is bonkers.
One of the things that was clunky in the React version was the use of setInterval. I had to write a hook in React and it just added this unnecessary layer of weirdness in how it all interacts[1]. In the Lit version, I just use setInterval normally and there's nothing extra to understand.
[1] https://overreacted.io/making-setinterval-declarative-with-r...
JS frameworks were invented to deal with clunkiness of Vanilla Javascript and got bloated to include everything and the kitchen sink. All of a sudden they're 'ecosystems'.
Modern JS + Web components with the light layer of Lit on top solve at least 80% of the issues there were and are with just plain JS. Close to actual web standards and thus play nice with almost everything. By design they fit into a far more modular approach than React or Vue or Svelte.
I made sure that all of the interactions in our app that don't require a server round-trip are instant, without any annoying undead skeletons and animations. This works really well because we keep most of the data in RAM on the client, with IndexDB as the backing store and a custom synchronization protocol.
I avoided the "server-side rendering" out of a general distrust of "magic" solutions that do everything for you.
React itself is also really straightforward as a mental model of rendering.
AI writes React well too
Here is a fun thing.
Write a bunch of JSON. A lot. Now write a lot of JSX. Then convert the JSON to JSX. Then convert the JSX to JSON. I was surprised by how much easier it was for me to reason in JSX. I use threejs and react three fiber (r3f). Again the JSX type of representation easily wins out for me. I don't really understand why. Maybe JSX ends up being more pictorial - as in a picture is worth a thousand words?
So I'm not sure I even care of about React. I just reason better with JSX than with all the other crufty things (template, html, htmx, etc). And yes, find all of them including React crufty.
But they’ve honestly not worked out.
React works have been a lot better if the React team has figured out what useEffect was supposed to be right at the beginning, or at least when they changed what it was supposed to be used for they documented that clearly instead of gaslighting devs into believing nothing has changed.
Also, hooks aren’t a thing. Each hook is its own API and concept. Grouping them under the umbrella term “hooks” gave the wrong impression that it was a single concept, but there’s No conceptual similarity between ushered, useState or useEffect. The only similarity are the restrictions on their use and nomenclature.
Then Svelte 5 came along and made Svelte more like React. At first, there were just a few simple runes, but then the runes started proliferating like crazy to solve other runes' problems. At that point, Svelte was dead to me and I went back to React/Next.
The right path for Svelte to take would have been to continue to refine Svelte 4.
I don't want to be a "you should've double bagged it" guy, I'm just curious. Svelte is not the be all and all, if you moved on to greener pastures more power to you.
I've helped juniors learn React as part of their studies and when they then come to intern with me they become happier once they learn other ways of making things happen on the screen.
Then they took it to the next level with things like styled-component. Virtual DOM is just an implementation detail and overrated to the success of React.
Any future web framework that solves that component thing and allow people to just write code instead of "web code", they'll win.
(I am aware that the web veterans don't like that view)
But the thing is, React and others is useful only for a few specific cases, IMO. I would only feel the need for them if we're building truly interactive applications (Open Street Map, figma, a text editor,...), but only because they've taken care of the state management boilerplate (even if you're now boxed by their applications. But most apps on the web don't needs to be an SPA. They can actually be improved by being a multi page application with small islands of interactivity.
I also hate React for many reasons, but I mostly like it because I genuinely believe React is the only front-end system I've used that got the fundamentals correct - they've just gotten many other trivial-but-still-annoying things wrong.
The fundamentals:
- no magic: this is a trend that the JS community has picked up I think from Ruby or maybe just Rails, but having a framework pick things up automatically depending on how you've placed them (directory structure, DSL files without explicit exports, etc.) means you need to learn each framework's magic independently & upgrade paths are harder when magic changes. React is just javascript - components are stored in variables, your app container is a native dom primitive & you need to call the render explicitly. This makes your entry points clear & idiomatic, & ensures your framework stands the test of time.
- composable (& somewhat fungible) primitives: React components are JS objects, there's certainly some elements of the underlying lifecycle implementation that's abstracted away once they're passed to the rendered, but at definition time they're just objects - they can be passed around, inspected, manipulated & ultimately they're not abstracted away & hidden from the dev.
- modular DSL: I'm suspicious of DSLs in general but if you're going to create a DSL, JSX is a masterclass in how to do it. 100% independent of React - you can use JSX without React or React without JSX. This modularity has lead to JSX getting broad high-quality third-party tooling support that all other front-end frameworks lack.
- imperative DSL. This is a bonus but the design of JSX is ingenious. Declarative systems are the dominant trend: everyone wants to create a well-defined strictly static language to define their front-end, but that design goal doesn't match reality. JSX isn't declarative: it's an extension of javascript, tags are function calls, attributes are params. This makes it incredibly expressive - I could write my backend in JSX if I wanted to (could be a fun experiment).
The bad:
- class-based components. This is less of a React problem & more of an ECMA problem but shoehorning Java-style classical inheritance models into javascript was always a bad idea & React was the main proponent of this style when it was popular. Thankfully we've moved past this phase & modern React no longer does this.
- lifecycle methods: these were tied to class components so were always going to end up becoming deprecated when classes went away but they also had other annoying fundamental issues - too complex to go into in detail here but their operation was highly abstracted which made debugging really painful
- hooks: the modern replacement for much of the lifecycle method functionally isn't much better. Thankfully hooks are mainly used for state management, which has always been an "added extra" in the React world (see e.g. flux), so you can minimise your hook usage & avoid a lot of friction if you're clever & careful.
Server components, state management libraries, data fetching libraries, routers, etc, are horrible. Same with most UI libraries. They add so much overhead and complexity.
But React is really, really good at the thing it was designed for: building interactive UIs. You can even build native apps. Can you build a native app in htmx?
Most of the alternatives to React are simplifying alternatives. They aim to take complexity away in order to solve a subset of basic use cases with less ceremony. Sure, if you have uncomplicated needs then use whatever solves your problem, web components, HTMX, Jinja, plain HTML, markdown: go as low as you like down the hierarchy and choose the simplest thing that could possibly work.
But nobody as of yet has built an alternative to React that can solve the difficult use cases better than React. Au contraire, React has moved into domains that it was never even designed for. Native apps. Terminal apps. That's the power of a good abstraction.
React is the worst JS framework except for all the others we've tried.
I'd take React over the Angular 1 days any time. I'd take Angular 1's full-bodied MVC over the "build it yourself from scratch every time" approach of Backbone. I'd take Backbone's minimal MVC structure over the classic JQuery Soup architecture. And I'd take JQuery's dom manipulations and standard-library improvements over the native apis (of that era) in an instant.
React has its tradeoffs, but we got here after a long slog of other things that don't work.