+1 React DX is really great. It started really great and it got weird and bloated but it's still really great relative to the JS landscape hell.
But, also yes, it's a pain in the ass and a frustrating kind of necessary evil. So there is room for improvements.
Nextjs is a living hell. The ironic thing is AI makes it dramatically more tolerable to the point it's actually pretty good. But that can't be a good thing in terms of architectural design can it? We're in odd times.
Of course, it's easy to be a hater on the sidelines. I am guilty. Nextjs likely just does too much in it's own made-from-scratch clever way. use-client vs server is just out-of-the gate ridiculous. But then I suppose the real question is "well if you want isomorphic environment, how else are you going to do it?". My answers is "I wouldn't!" but then vercel and the powers that be seem to have won the mindshare of the product crowd. At least for now.
Honestly I think React DX kinda sucks, at least in some areas. Performance is one of the worst (`useMemo` and `componentShouldUpdate` are way to easy to ignore, constant re-renders are the norm and writing performant React code requires conscious effort to avoid footguns) but it's also just less self-explanatory than the alternatives I've tried.
I started doing web dev before reactivity frameworks were a thing, and I found Vue to be the most intuitive of the frameworks available when I first needed reactivity. To me, Vue feels like HTML with superpowers while React feels like a whole new way of thinking about webapps. I'm honestly a bit surprised that the article doesn't mention Vue, since Vue is (and has been for a while) the most popular "not React or Angular" framework option. Newer versions of Vue even support the "disappearing framework" feature Svelte was built for, which I'm excited to take advantage of when my biggest work project finally moves to Vue 3.
I think you've nailed it. It does come down to user preference.
React _is_ a whole new way of thinking. Back in the days of jQuery it was very painful to stitch together web experience across HTML+CSS+JS. jquery provided much needed DX around utilities to navigate across these pieces. But it was still way too easy to treat HTML like your database and model user-state across a Frankenstein of server, json, html, and javascript.
React was a paradigm shift. "Screw it, everything is javascript." The web depends on js runtime, we know we're going to need it. It starts to makes the best future-forward sense to use the only full programming runtime available. From DX pov this was spectacular to speed up prototyping. And you can finally truly model state purely from data.
What followed was a huge a mess (redux), but I always say, what do we expect? The web is a mess, and it's great because it's useful. Humans are a mess!
---
VUE: similar to angular I just don't align with "super-powered html attributes". It just doesn't make sense as a mental model. Because it's _not_ HTML spec and HTML is not a programming language. The more powerful the framework gets the more we reinvent a pseudo-programming language _through_ HTML. Angular was full-stop a no-go when I first saw it's for-loops in HTML.
Neither react's JSX nor vue's template language are HTML. But rejecting vue's template on grounds that it's not HTML seems odd. React's JSX deviates from HTML in many ways. Like class vs className. XML self-closing vs HTML self-closing. onchange vs oninput. On purely aesthetic grounds, I can't understand how the react idiom of array.map() would ever be preferable to an affordance in the (non-HTML) template language for handling this normal standard thing that always happens.
it's not about feigning html purity it's the opposite. Why pretend we're using HTML when it's not? so with react it becomes a js flavor, jsx, which some people hate but it's very clear that it's a made up language IN real javascript.
edit: the mental model is instant: it's just javascript for reals. do anything you want in javascript using real js primitives. it's not about looking pretty, jsx doesn't. it's about not relearning basic programming primitives in a made up _markup_ language.
my issue with angular is it's neither real html nor any programming language. its made up pseudo-programming language for no other reason than it fools people into thinking "it's just HTML". that's my gripe.
Completely agree with you. Every time I see yet another template language adding some clumsy for-each loop syntax I sigh. Just let us use a normal programming language. As an example I give you every template system ever invented. Devops tooling is full of them.
Over the years, I've seen a few posts like this that seem to take it as a given that a loop in a normal programming language is better than foreach capability in a template language. Certainly enough times to believe that a significant group of people actually believe it's superior.
There's not a difference in capability of expression of the two models. It seems to be a purely aesthetic or comfort difference.
It's because native programming language will defacto allow you to hack it to its natural limit. A tendency most all programmers have given they even get into programming.
For example with any iterator/loop you may want to filter, or find, or transform. in ruby you have the entire Enumerable API to dig into or Array prototype for js.
a templating language would have to reimplement functionality one by one in an allow list.
it's just fatigue at that point, yet another API i've got to mentally track.
edit: of course if you export the view data "clean" before hand it compels you to not have intense logic in the view. I get that but after a decade+ in product, views are never pure, even just ability to highlight the active tab takes conditional and select logic in a loop.
I would rather that all the developers were "encouraged" to do the filtering and sorting in some kind of logic block rather than having an attractive footgun lying around that makes it easy to cram in one more last data adjustment.
JSX also fools people into thinking "it's HTML in javascript". I've heard several co-workers say this. JSX is a made up language as well. It's not javascript. That's why you need a build step to parse the syntax.
Angular and vue's template language are no more made up than JSX is.
React DX is probably the worst DX of all frontend frameworks I've ever seen. All kinds of confusing concepts (hooks, memo, props passing, class style vs function style, an entire new "language" like JSX which you then first need to install an IDE extension for) and you cant even begin a react project most of the time without using some kind of react starter template because building this thing is so hugely complex that people just give up. Then you constantly run into issues of double rendering with reactjs which is quite hard to debug. I was hoping for something tiny like https://www.arrow-js.com/ taking off but the creator doesn't really work on it.
Next initially jumped the shark when they went all-in on server-side rendering. The reason why Vercel did this is clear: client-side rendered apps can be hosted basically for free on Firebase, Cloudflare, or S3, so the only way they can raise their Vercel cloud revenue is by forcing their users into a dynamic-first world, pushing so much complexity and dynamism into the framework that only Vercel could disentangle how to host it effectively. The less-dystopic reasons they communicated as to why customers might want SSR; improved time-to-first-byte and a more PHP/Rails-like programming model; while well-intentioned, ultimately became of questionable value to customers given their choices during implementation.
I do actually believe a more PHP/Rails-like programming model would be beneficial for React; Vercel just missed the extremely important detail in how Rails is so dang productive. Its not their decisions when it comes to HTML templating; its Active Record.
I had a friend rave to me about Next because, to him, page load times were absolutely essential for good search engine placement. I wonder LLMs will make this sort of SEO less important.
I don't really recommend isomorphic environments, but if it's your cup of tea, Tanstack Start is making a lot of progress. It removes all of the magic and misdirection of Nextjs and just provides a good light alternative.
Issue with all things tanstack is they change everything constantly. The Tanner guy really does make decent libs but he drops em pretty quickly for others to take up maintenance on which makes it risky to pull into any production app.
React DX is hot garbage. Words cannot express how much I LOATHE hook rules. Coming from a Solid JS background, where reactive primitives are just Javascript functions... I groan every single time I run into (yet another) hook rule.
I have to conditionally render empty fragments because React can't handle conditional hooks. It's the stupidest thing ever. "Oh hey let me allocate memory for this hook that will almost certainly never be used except under edge conditions! Sure, React can do conditional components, but conditional hooks are just too much for us!"
Not sure I understand the conditional beef, perhaps you can give example? I would assume if you want `if condition, useEffect(...)` you could simply replace with `useEffect(() => if condition...)`, no?
Fair. My bitching would've been better expressed as "I groan every single time I attempt to violate a hook rule." Which is a lot, because I'm new to React. It's almost certainly a "skill issue", but hooks are NOT just "JavaScript functions", contrary to React marketing PR.
My conditional beef: in my app, users can choose between using the built-in mic for speech recognition or a 3rd party service (LiveKit). If the user chooses the built-in mic, I still must allocate memory for LiveKit's services because it's exposed as a hook, even if the user will never use it. This problem compounds - every option that I expose that uses a hook requires that I allocate memory for an option that may never be used. Also TTS - users can choose to use the phone's TTS, or a remote service, etc. Every option I offer, if the library exposes it as a hook (and they virtually always do), if I naively implement the feature, allocates memory for a hook that might never be used.
Fuck. React. Hooks.
My workaround is to conditionally render empty fragments. These fragments wrap hooks, which I then inject into the context. This makes it so I can conditionally run hooks. This is why I complained that React can handle conditional components, but not hooks. Concretely: https://pastebin.com/sjc3vXTd I'm using Zustand because god I need a lifecycle outside of React.
Y'know how people complain about how Async "colors" functions? Hooks are also a type of function coloring. And they don't compose with Async.
Yeah, this is a really annoying thing about how hooks work. For whatever reason (I'm sure they have a great reason) React can't do hook state book-keeping correctly without tying it to a function component lifecycle.
I think you actually can conditionally render a hook but that choice has to last for the entire rendered lifetime of the component. But that doesn't really help you when your user can switch between them.
Hooks can call other hooks, and all the built-in hooks rely on setState at the bottom. setState is state for the individual component. It keeps track of state across multiple calls with an indexed array - the first call is index 0, second call is index 1, and so on - that's why no key is needed to identify which setState call is which.
All the oddness about hooks fall out of that implementation. They can only be used inside components because they rely on component state management, and having to be called at the top level is a simplification of "hooks must always be called in the same order the same number of times" (no conditionals or loops) because otherwise the setState index gets messed up and you're getting the wrong state back when it's called.
You don't have to use their hooks! Looking at your pastebin link, I would probably opt for something like a factory pattern instead: https://pastebin.com/PbnBqX4a
Just because you're in React land doesn't mean you can't still write regular old js/ts and hook in only when you need it. I imagine you'd do something quite similar in any other framework.
It only takes 1 hook to pollute your entire factory pattern; the comparison to colored async functions wasn't spurious. Hook-only options seem especially prevalent in the React Native ecosystem (ironic, given the memory constraints of phones).
Of course, I could fork/go down to the native layer, but this just proves my point that React DX is hot garbage.
No, sadly. I use some features that require the usage of native code, e.g. native echo cancellation, foreground service to keep the audio running when the screen is off, etc. The sdk-js is meant for browsers.
Based on how they are run they are completely not just ordinary JavaScript functions, hook era components are also not just JavaScript functions, it's a very complicated system. React calling them "just functions" is untrue, just marketing buzz words, and it leads developers into traps.
Many functions can only be called in a certain context. Calling them "not functions" is misleading imo because it implies those functions are compiled out or something, like `$state()` in Svelte.
Yeah they themselves are functions but how they're called are managed by a complicated system, I think treating them as a separate new concept is less misleading than calling them plain functions
Well they aren't plain functions, they're like lifecycle methods for the component with an implicit `this`. Perhaps that's how they should be described.
I'm pretty sure this is also untrue. AFAIK React has never used that phrase (and at the very least, I can't find it anywhere official right now), it came from other people convincing newcomers that hooks aren't something more complicated like objects (comparing to class-based components). React has always treated them as special functions, hence always prefixing them with the word "use".
They kind of are not though, you can't call them out of order and other things which is checked at runtime by the React "engine" and will stop script execution. If they were regular functions you could call them anytime.
They are context dependent, must execute in the same order every time, and must be called every time the component re-renders (i.e., they do not support conditional calls). They have enough gremlin rules that calling them "just functions" is unhelpful for reasoning about using them.
But, also yes, it's a pain in the ass and a frustrating kind of necessary evil. So there is room for improvements.
Nextjs is a living hell. The ironic thing is AI makes it dramatically more tolerable to the point it's actually pretty good. But that can't be a good thing in terms of architectural design can it? We're in odd times.
Of course, it's easy to be a hater on the sidelines. I am guilty. Nextjs likely just does too much in it's own made-from-scratch clever way. use-client vs server is just out-of-the gate ridiculous. But then I suppose the real question is "well if you want isomorphic environment, how else are you going to do it?". My answers is "I wouldn't!" but then vercel and the powers that be seem to have won the mindshare of the product crowd. At least for now.