This article hits on something I've felt for a long time. The idea that "hooks are superior" to me is ridiculous. If a linter is required to tell me when I'm writing a bug that is not immediately obvious, that is a failing in the framework to round those edges. Lints are not rounded edges!
Solid is nice and _seems_ to fix the issues with hooks, but as another comment mentioned, the challenge is with building at scale. It's unclear to me how this scales and where the sharp edges are.
React IMO trades off performance in exchange for ergonomics. These ergonomics bear fruit early on, but you start dealing with this debt very quickly. As "nice" as Redux is, I shouldn't need it so early. React is designed in a way that results in pretty terrible performance. I once wrote a React app, and discovered perf issues 2 weeks in. Any UI framework I used in the past would have scaled beyond this point without having to have the data layer rewritten. Frameworks of the past also had what seemed like way less "magic".
I totally accept that it's seriously nice to write, but how many trees has React alone burnt?
React traded one set of footguns for another set. This is also after they fudged with the class component lifecycle methods before tossing in the towel on that entire API.
> If a linter is required to tell me when I'm writing a bug that is not immediately obvious, that is a failing in the framework
yep. They also commandeered the entire use* namespace just so they could get lint to warn when you put a hook outside the start of a function. Hacks on top of hacks.
I have the same complaint about hooks. Most people seem to ignore that tidbit, but to me it's really frustrating. Plus I recently hit more hook issues when putting a setInterval inside a useEffect. There's no way to do a normal didMount/willUnmount workflow without other hacks (i.e. useRef) just to set up a simple timer. Maddening!
Edit: after writing this I went and read the article. Same scenario I was bitching about lol
React hooks is another attempt to gain ergonomics. The idea is to try to spread the virtual DOM into native effects. In theory the code specifies or declared the effects once and the framework takes care about subscribing/unsubscribing as necessary. But in practice this became so messy that in any complex cases one better stick to classes and explicit subscribe/unsubscribe.
The right way to do that exists in Elm. But I do not think there is a way to translate that into JS without requiring either a lot of boilerplate or very messy code
React would work well as a compiled to metal language. Instead we end up with React on Typescript on ES6 on ES5 on the browser that still only has one language targetable. We're shackled to a rocketship in motion desperately monkey patching the engines.
>If a linter is required to tell me when I'm writing a bug that is not immediately obvious, that is a failing in the framework to round those edges. Lints are not rounded edges!
Those linter warnings with React hooks show just how much of a disaster it is. All of the "simplicity" that comes with hooks is just complexity that is now completely out of sight, and only appears once the code is run in a linter. That is even worse than having the complexity in the code.
After working with React for years it is something I would not recommend and can only think of limited use cases to use.
You can write really performant software in React, but ergonomic (beyond stockholm syndrome) I would not call it.
It's also difficult to implement quality software engineering principles in a React application such that an application is maintainable, glacable and easy for someone new to a project to pick up.
>"If a linter is required to tell me when I'm writing a bug that is not immediately obvious, that is a failing in the framework to round those edges".
I do not want this handled at the framework level though. There are plenty of times in my day-to-day where that linter is wrong. If I were to add one of the dependencies (it's sure I should add) my component would re-render over and over. Sure, one could argue, "well, then you've poorly architected your code"... but THAT is where I feel like the failure occurs. Having to do write my code to please a paradigm. What that means is I only "sorta agree" with the design trade-offs. And much like Ruby on Rails, you either go all in, or spend your days hating the framework.
I do worry that Solid also has these "If you know, you know" edges though. So I'm trading looking at a function and assuming it'll run over and over with, looking at a function and thinking, "how can I make this thing run again when it needs to?" I'm not saying it's wrong, it's just another piece of tacit knowledge one has to learn when adopting a new framework.
With Class components it was often easier to create Pure components, or do a shouldcomponent update check to optimize performace. They later introduced memo for functional components.
> If a linter is required to tell me when I'm writing a bug that is not immediately obvious, that is a failing in the framework to round those edges.
Isn't the point of static analysis (including linters) that they catch those kind of bugs? Having that little red underline (or console warning) saves a lot of pain.
Hooks have completely changed the way I write React code. It's so much easier to test and reason about. Really the only problem is that the guardrails aren't more heavily enforced.
I don't think complaining about needing a linter makes sense. JSX is already a compiled language, so you have a compile and build step (doubly so if you're also using Typescript). Think of ESLint rules-of-hooks as just a plugin to your compiler that further modifies the syntax of JavaScript to include rules about when and how you can call certain functions.
Hooks are a syntactic element of react code. Calls to them aren't legal inside conditionals or loops.
Full linting support smooths over that rough edge just as effectively as babel handling ?. syntax smooths over its absence in certain JS runtimes.
New JS frameworks always make for compelling hello world examples.
Can you branch on state or use loops over data in Solid.js? The reason _why_ React has a virtual DOM is to enable more interesting relationships between your data and your presentation. Anyone can make a framework that makes the source code for an incrementing number look pretty!
As an example of this point, check out the "Simple Todos" example for Solid.js[1].
In React, we render lists by using regular JavaScript idioms like loops, arrays, and array methods like map. However in Solid.js, much like traditional templating languages, we get a construct like <For> that reinvents a concept that's already in the language.
I've been writing React and React-alike code for a long time. I think that fine-grained updates avoiding reconciliation are a good idea, especially for performance. At one point, I built a React-like library for Roblox and Lua whose most novel feature ended up being "Bindings"[2], which look sorta like Solid.js state containers. They create little hot-path data dependencies, but the bulk of your components still use normal React-like rendering.
I've used React for ~3 years, primarily with function components and hooks. I think that hooks were a wonderful addition and I think the framework has made smart choices with checking object equality to decided if components re-render.
That said, I think that easily the most difficult aspects of react revolve around how re-renders are triggered. Maintaining referential equality to stop unnecessary renders gets tricky when you are passing functions or objects. Suddenly you need to be using `useMemo` and `useCallback` and passing dependency lists that all have to be primitive values unless you want to memoize them as well. It can become such a headache that the official line around it mostly seems to be "make the render fast, don't worry about unnecessary re-renders" – good advice, until you hit a use-case where you need to worry.
Solid takes these problems and just vanishes them. UI state knows what its dependencies are automatically and only updates when they change – even in a sub-component level!
To be fair, I've never used Solid in anger, and moving to it would be a big ask when there is such a good ecosystem built up around react. That said it is easily one of the most exciting projects on my radar, and the developer Ryan Carniato seems extremely knowledgeable in the area.
> "Ohhh, an OO pattern with a couple of one-liner lifecycle methods is just WAY too much code! Higher likelihood for errors and worse developer experience."
...
> "So instead, I'm going to replace this with a functional pattern, that crams a couple of lifecycle functions into a closure, and is riddled with edge cases and common developer mistakes."
This article perfectly crystalizes why my career has tracked toward the backend over the past decade. All of the virtual ink in this article, and honestly most of the complexity in the field overall... and it seems to really all just boil down to, "I think this looks cooler."
I swear we're just going around in circles because people only have a surface level understanding of these front-end frameworks, and the challenges with building at scale.
react isn't about 'hooks', 'jsx', 'top-down-state', or 'component-driven architecture'. All these frameworks are component-based, can have top down state only (or do bottom up in react), can use things like jsx/hooks because it's just syntactic sugar (vue has jsx support).
react is fundamentally about 'inputs changed, render this'. This got rid of a lot of issues with poor code because frameworks had crappy DX (angluar 1 scope nonsense, and overengineered DI concepts), and people were bad at tracking side effects because a lot of people just wanted a search bar with some cool features and not everyone was building sophisticated products.
That setInterval example is fundamentally against what react is, and is basically svelte/vue/(react + mobx).
> react is fundamentally about 'inputs changed, render this'
I mean, pretty much all frameworks these days have that fundamental declarative model, react wasn't particularly innovative on that front (e.g. the declarative model already existed in angular, knockout, etc)
What the setInterval example highlights is that newer subsystems in React like useEffect and Suspense are bolted on top of earlier iterations that weren't originally designed to support these kinds of semantics, and the dissonance between API design iterations has become noticeable. This is a pain point that is relatively unique to React.
The growing popularity of Svelte and Solid are largely because their API designs align naturally with how people expect features to work, without people falling into pits of failure like stale closures and incorrectly wired dependencies. React is popular and it puts bread on your table and all, but pretending it doesn't have warts doesn't do anybody any favors.
This is just iteration on the awesome groundwork that React laid, and shows that things can still be much better. React has some very peculiar patterns that don't really jive well with javascript as a language, or its ecosystem. If the setInterval example is fundamentally against what React is, then IMO that really hammers the point the author is making. I have a few React projects under my belt, and often times still find myself confused by hooks or the mess that I create when I use them.
The thing is that While react is against side effects, javascript is not. Which result in these impedance mismatch where what devs want is against react itself.
Vue/svelte/solid do not fight against js, hence they do not end up in similar situation
> All these frameworks are component-based, can have top down state only (or do bottom up in react), can use things like jsx/hooks because it's just syntactic sugar (vue has jsx support).
Honestly, the boon of React is just how easy it is to create components, or at least how simple things were back in the day - it is exceedingly composable, moreso than AngularJS, Angular or Vue have been, at least in my experience. In React, your component can fit within a single file, containing simple syntax, especially for when you're making a pure functional component with no side effects or hooks. And even when you need to add something more complicated, you just have a method or two to change, essentially "progressive enhancement" for your code.
Though admittedly state management, or at least our current approaches to it ruin everything with endless boilerplate (Redux, Vuex etc.) to address an issue that may or may not be easier to represent, though some libraries certainly try (MobX comes to mind).
Of course, my experience leads me to agree with the article, in how React in combination of hooks sometimes is problematic, although in my case that was primarily because of render loops and how the stack traces are akin to JDK 8 NullPointerExceptions, where you couldn't see exactly what's causing you problems: https://blog.kronis.dev/everything%20is%20broken/modern-reac...
I'm probably wrong in liking class based components since those have other issues and Vue/Angular both feel a bit less productive in comparison, even if sometimes easier to reason about, with different tradeoffs to them. Maybe i should check out Svelte some day, but i guess it's all just one long slog of finding what works for you and what doesn't, much like it is with back end programming languages or even relational DBMSes.
React was about bringing a more immediate mode UI programming model to the web, as opposed to retained UI programming models are a PITA to work with. Retained doesn’t work well in games, it doesn’t work for productive UIs either.
That's not completely correct. React is, and has been from the start, a UI rendering library. Its fundamentals are component abstractions, the component tree, lifecycle, the virtual DOM. We had other declarative frameworks before React (Ractive.js, svelte's spiritual grandfather, being the most popular one).
The 'inputs changed, render this' paradigm (and by this I mean reactivity, not the declarative model) has been around for much longer and is exactly what this post is about - React doesn't really do that, since it relies on you to explicitly tell it, via dependency arrays or setState calls, when to re-render. It is not fundamentally different from `.on('change', this.render)` code we were writing back in 2010, just a lot of syntax sugar on top.
That React managed to sell itself so well, while not actually delivering on the reactivity or performance promises, is the surprising part. I'm excited for the future as we finally move on from this era.
I keep my sanity by ignoring all of them, focusing on mastering pure Web standards only, and delving into such frameworks only when I am required to collaborate with Web FE devs.
This is about "feels like" and me too--the little newsletter popup and lack of substance shows this. React became obsolete over the past few years as browsers increasingly adopted the mix of web components features (eg componentDidMount vs connectedCallback). Just using React, and the explanations for why a technology is and isn't used in an organization speaks to the level of practical knowledge and detritus in projects. That products survive this long with React illuminates how resistant the community is to... reading and doing work in the easiest, pragmatic way possible. Like why bother writing an article like this?
Slightly dismissive, but I agree the setInterval function was a little misleadingly contrived.
The transition to functional components was to reduce the coupling between abstract functionality and DOM-related lifecycle events.
React hooks are mostly about expressing where and when you want to memoize a value, with the default being not to.
Once you learn what to look out for, and properly designing and review codebases at scale, these trivial issues don't happen all that often. Additionally, being able to specify memoization parameters explicitly brings extra flexibility and some additional design patterns.
The original angular worked perfectly for me, but that's because I loaded the source code onto my tablet and read the entire thing top to bottom over a few evenings in a pub beer garden with a cigarette and a pint to keep me company.
Newer frameworks are absolutely better for anybody who isn't sufficiently batshit enough to do that, but much though I enjoy react + mobx (especially react + mobx-state-tree) I've never got to the "I have the core source code in my head and can mentally dry run it as a desk check type operation when debugging" stage with them like I did with early angular so - with the level of jank inherent in its scope nonsense entirely acknowledged - I still occasionally miss it even so.
(this is mostly me being nostalgic, I think, the newer stuff is absolutely better but that was a fun few evenings and for its era damn but I could make that thing sing)
I think for progress you need to look outside the js world. Fundamentally, people keep repeating the same mistakes there.
I'm having a lot of fun lately using Kotlin-js for example. We use the Fritz2 framework, koin for dependency injection (popular on Android as well for good reasons), and fritz2 relies on kotlin's co-routines and StateFlow for state management. It makes for a surprisingly concise code base. For example, the counter example from the article with that would look something like this:
class CounterStore : RootStore<Int>(0) {
val koinCtx by lazy { GlobalContext.get() }
// handler that you can bind events to or invoke directly like below
val inc = handle { old -> old + 1 }
init {
// launch co-routine to keep on incrementing the counter
GlobalScope.launch {
while (true) {
inc()
delay(1000)
}
}
}
}
val koinCtx by lazy { GlobalContext.get() }
fun RenderContext.counterComponent() {
val counter by koinCtx.inject<CounterStore>()
h1 { +"A Counter" }
// react to changes in the counter
counter.data.render { currentCount ->
p {
+"Current count: $currentCount"
}
}
pushButton {
icon { arrowUp }
events {
clicks handledBy counter.inc
}
}
}
fun main() {
startKoin {
modules(
module {
single {
CounterStore()
}
})
}
render("#target") {
counterComponent()
}
}
There's a lot going on here that I can't explain here. But having co-routines means having a proper reactive framework that you use to react to events and update stores, which is where you keep your state. counter.data is a so-called StateFlow; the render function maps updates in that flow to the dom. In the example I have both a button and a co-routine updating the store via a handler lambda function.
Using koin here, just means keeping glue code out of places where it doesn't belong. It's technically optional but makes a lot of sense in larger applications. Because components are extension functions on RenderContext, I use a global variable to get to the koin context. That allows me to inject my dependencies into components with a minimum of fuss. Where Fritz2 gets fun is with more complex state using data classes, lenses, validators, routers and a few other things. And they also take care of styled components and they even have a nice component framework that you can use. Not for everyone and there's a bit of overhead in terms of download size. But great if that less of a concern.
I'm proficient with React hooks and functional components, but I will never bullshit anyone by pretending that they make any kind of intuitive sense.
Classes may have been more "code" but were clear to understand. I don't dispute that functional components are probably more efficient though.
Anyways, I've never had this problem with React hooks. I've been building complex dynamic UIs for years, and maybe I've grown to get around these kinds of things, but it seems like Solid.js is solving a niche problem at the cost of making hooks even less understandable?
I've tried using bare React in the past (after using Clojurescript), because I wanted my project to be more approachable for outsiders. But I couldn't really handle the (to me, and the author) unnecessary complexity that's added.
I would even say the Reagent version is even simpler than the Solid.js version, because you're using Clojure's Atom API rather than creating read / write functions. For the adventurous hearted I'd definitely recommend giving it a try!
Like the author of this post, I appreciate Solid's API because component's only render (i.e. run) once by default and then you define which sections of the component should re-render on changes by using "signals" provided by the library (e.g. `createSignal()` and `createEffect()`). In react, the entire component re-renders on every change and you need to specify which code should _not_ re-run. This was necessary because of the way react was created, but strikes me as fundamentally flawed.
Having used Solidjs for some pet projects, I've come to strongly prefer Solidjs over React. It's an evolution of react, so I've found my existing skills/knowledge transfers. This being said, Solidjs is brand new and the ecosystem is minuscule compared to React. For this reason, I plan to continue using React for the foreseeable future. One of the biggest weaknesses of Solidjs is the lack of a "nextjs" like framework. It appears work is being done in the solid-start[1] repo, but it looks like it's still years away from being fleshed out. I want Solidjs to succeed, but I'm not interested in being an early adopter.
This is interesting because it's a bit like Svelte, but it doesn't use the Svelte "language" (which looks like JS and HTML but, in some crucial ways, sometimes isn't).
It's a little disheartening to see that it's 3+ years old and has only had a single significant contributor though[1].
It's impossible to avoid single-contributor projects in the JS world, especially with Node, but the alternatives (React, Vue, Angular, and even Svelte) are orders of magnitude more popular, so it's one area that we can play it safe if we need to.
"That’s a lot of code to write for an auto-incrementing counter"
In reality, you will never need to write an auto-incrementing counter :) React gives you a mental framework, you draw a page based on the state in a declarative way. The clever abstraction you make, the less you write the code, so it's a little bit pointless to compare it with an auto-incrementing counter application, in reality has no use case at all.
For anyone who wants a thorough understanding of how to think about React hooks in the context of things like setInterval, Dan Abramov wrote a great piece breaking it down in detail several years ago: https://overreacted.io/making-setinterval-declarative-with-r...
This post helped hooks "click" for me, and once it did, I've absolutely loved them and now thoroughly enjoy writing custom hooks that greatly simplify my code.
Honestly my hope (and I admit as a full-stack but leaning back-end developer to be biased against JS) is that the future is in things like turbo-stream, stimulus reflex, phoenix liveview etc - or in things like all_futures (essentially an ActiveRecord wrapper around kredis) - that we move towards building reactive-apps by firing off events from the back-end and figuring out how to subscribe to them on the front-end
the amount of confusing boilerplate I've seen to keep updated and maintained when a JS framework is loading front-end state by making API requests against a backend and then trying to figure out how to keep those in sync when we could just be firing off SSR HTML over the wire and/or very thin events that FE components can subscribe to or emit for literally no gain in functionality is beyond me
even better, just add reactive sprinkles over what you need reactive and do the rest with standard MVC/REST patterns, if most of what you are using react for is glorified forms, you don't need react for that! user your reactive sprinkles of notification toasts, and chat channels...
Almost every criticism of React that I've read, goes away when you drive it from Clojurescript. If you haven't had the pleasure I suggest you take it out for a spin.
Example of a full blown React component in Clojurescript:
(defn counter
[]
(let [state (r/atom 0)]
(fn []
[:div {:onClick #(swap! state inc)}
"The count is: " @state])))
We've used React with Mobx in commercial apps for 5+ years now. React has been good for us BUT Solid is so much cleaner (and leaner).
Porting most React code to Solid is pretty easy - mostly involves deleting things that are no longer required (useCallback, useRef, etc) and moving prop access into the JSX to enable just those specific attributes to be updated when state changes.
It has come to the point where I really begrudge going back to working in our React code. Unfortunately those apps will still be around for a long time - but we won't be using React for green-fields projects.
I've been using React since around 2016 and when Hooks were introduced I played around with it but didn't like it at all exactly because of the reasons mentioned in this article. Sure it reduced quite a bit of boilerplate code that came with class based components and enabled better code re-usability outside of class inheritance or mixins but the disadvantages were too big for me personally to really consider using Hooks in a "greenfield" setting.
In my current project I'm using Vue with Composition API which gave me the exact same "aha" moment the author of this article had with SolidJS. Vue + Composition API is way more similar to SolidJS in principle than React + Hooks.
Great majority of my projects are data dashboards and I struggled with React for some time because of its top-down model - it can hinder performance really badly. If one of your points on scatterplot should be marked, you need to carefully pass this prop through few memoization layers, so any other points won't try to re-render.
I've been on this quest for a long time, because I like React model. So I had fun with putting observables in my app (kefir and karet from calm-js and my own thing with xstream), I tried custom selectors with redux and all 'like-redux-but-simpler' libraries, I also tried recoil. These solutions can work and worked for my apps, but it felt like I was fighting against React.
Solid was nice. It provided necessary tools to write interactive data visualisations with ease and in performant way. It has quirks but they are manageable - my team also learned and contributed to projects in Solid.
First, the concept of "component functions" that are more like factories. It is not groundbreaking (reagent had this idea long ago) but is quirky. Thankfully, half hour with Solid playground and everybody can see what code is generated and how it works under the hood. It is really predictable but also tangible - you can play with just part of your app in playground if something is unclear.
Second quirk is props object. I understand the trade-off but it trips users (me included) that you can't just treat it as regular object. Sadly, only solution for this is lint rule - yuck. But it is much simpler rule than hook rules - just don't destructure props.
In the end, Solid is great tool for "web apps". Think about dashboards or diagram editors. Cheap components, fine grained reactivity, focused updates yield great performance results without jumping through hoops.
Slightly OT: I’m always amused coming to discussions like this one, where people are (basically) complaining about the Virtual DOM and its implications. It’s bad for performance at scale, updates aren’t minimal. And other stuff: SFC are hard to wrap your head around, I don’t want to write explicitly reactive code and so on. Some framework solves this by looking (somewhat) like React but in the end doing something entirely different.
May I interest you in Angular? It certainly isn’t cool and I really do hate it. But especially since the AoT compiler has been implemented, performance is quite good. There’s templates, which some folks seem to love. Angular keeps in-memory references to all dynamic elements in a template so they can be updated with high efficiency. It has class components. It has a lifecycle method that is called OnInit. So maybe give it a whirl.
I find Solid.js a "better" React as well. In a new project that needs to be used by a lot of team-members, however, I still chose React.
Why? Because of the ecosystem! Do I need accessible, headless components? Use React-aria from Adobe! Do I need state management? There are many established ones, I just need to follow their best practices. Everything supports React, every hire speaks React, and it works, not like "just works", but "... works" and, disappointing to the engineer inside me, this is not something I can trade in big projects.
As with any new JS framework technology, it will feel great until its flaws, warts, deficiencies and limitations are inevitably discovered as complexity rises, and which are then addressed in the next JS framework.
Can anyone explain the point of the virtual DOM and why it’s not a source of huge performance issues? What I mean as soon as you retain a reference to a DOM element, it’s lifetime becomes managed by the JavaScript gc - let’s say you build a paginated gallery app in pure HTML and an spa - in the first case, the browser knows that as soon as you navigate away from the page, all those image thumbnails are free real estate, at the latter case it first needs to wait for the spa to release references to the underlying html objects, then the gc needs to run, at which point it can get rid of it.
[+] [-] parentheses|4 years ago|reply
Solid is nice and _seems_ to fix the issues with hooks, but as another comment mentioned, the challenge is with building at scale. It's unclear to me how this scales and where the sharp edges are.
React IMO trades off performance in exchange for ergonomics. These ergonomics bear fruit early on, but you start dealing with this debt very quickly. As "nice" as Redux is, I shouldn't need it so early. React is designed in a way that results in pretty terrible performance. I once wrote a React app, and discovered perf issues 2 weeks in. Any UI framework I used in the past would have scaled beyond this point without having to have the data layer rewritten. Frameworks of the past also had what seemed like way less "magic".
I totally accept that it's seriously nice to write, but how many trees has React alone burnt?
[+] [-] deckard1|4 years ago|reply
> If a linter is required to tell me when I'm writing a bug that is not immediately obvious, that is a failing in the framework
yep. They also commandeered the entire use* namespace just so they could get lint to warn when you put a hook outside the start of a function. Hacks on top of hacks.
[+] [-] oaxacaoaxaca|4 years ago|reply
Edit: after writing this I went and read the article. Same scenario I was bitching about lol
[+] [-] _0w8t|4 years ago|reply
The right way to do that exists in Elm. But I do not think there is a way to translate that into JS without requiring either a lot of boilerplate or very messy code
[+] [-] ipnon|4 years ago|reply
[+] [-] kccqzy|4 years ago|reply
That said, I still preferred React.createClass so I guess I'm somewhat off the beaten path when it comes to React.
[+] [-] leonardopainter|4 years ago|reply
Those linter warnings with React hooks show just how much of a disaster it is. All of the "simplicity" that comes with hooks is just complexity that is now completely out of sight, and only appears once the code is run in a linter. That is even worse than having the complexity in the code.
[+] [-] apatheticonion|4 years ago|reply
You can write really performant software in React, but ergonomic (beyond stockholm syndrome) I would not call it.
It's also difficult to implement quality software engineering principles in a React application such that an application is maintainable, glacable and easy for someone new to a project to pick up.
The flexibility in its toolchain is nice, however
[+] [-] sbergot|4 years ago|reply
I still like using them however. I had a few issues when trying to do more abstract code.
[+] [-] nobleach|4 years ago|reply
I do not want this handled at the framework level though. There are plenty of times in my day-to-day where that linter is wrong. If I were to add one of the dependencies (it's sure I should add) my component would re-render over and over. Sure, one could argue, "well, then you've poorly architected your code"... but THAT is where I feel like the failure occurs. Having to do write my code to please a paradigm. What that means is I only "sorta agree" with the design trade-offs. And much like Ruby on Rails, you either go all in, or spend your days hating the framework.
I do worry that Solid also has these "If you know, you know" edges though. So I'm trading looking at a function and assuming it'll run over and over with, looking at a function and thinking, "how can I make this thing run again when it needs to?" I'm not saying it's wrong, it's just another piece of tacit knowledge one has to learn when adopting a new framework.
[+] [-] cies|4 years ago|reply
On the JS/TS planet there's https://cycle.js.org , which comes close.
Looks even better than Solid IMHO.
[+] [-] toshk|4 years ago|reply
[+] [-] schwartzworld|4 years ago|reply
Isn't the point of static analysis (including linters) that they catch those kind of bugs? Having that little red underline (or console warning) saves a lot of pain.
Hooks have completely changed the way I write React code. It's so much easier to test and reason about. Really the only problem is that the guardrails aren't more heavily enforced.
[+] [-] jameshart|4 years ago|reply
Hooks are a syntactic element of react code. Calls to them aren't legal inside conditionals or loops.
Full linting support smooths over that rough edge just as effectively as babel handling ?. syntax smooths over its absence in certain JS runtimes.
[+] [-] unknown|4 years ago|reply
[deleted]
[+] [-] joshribakoff|4 years ago|reply
[+] [-] lpghatguy|4 years ago|reply
Can you branch on state or use loops over data in Solid.js? The reason _why_ React has a virtual DOM is to enable more interesting relationships between your data and your presentation. Anyone can make a framework that makes the source code for an incrementing number look pretty!
As an example of this point, check out the "Simple Todos" example for Solid.js[1].
In React, we render lists by using regular JavaScript idioms like loops, arrays, and array methods like map. However in Solid.js, much like traditional templating languages, we get a construct like <For> that reinvents a concept that's already in the language.
I've been writing React and React-alike code for a long time. I think that fine-grained updates avoiding reconciliation are a good idea, especially for performance. At one point, I built a React-like library for Roblox and Lua whose most novel feature ended up being "Bindings"[2], which look sorta like Solid.js state containers. They create little hot-path data dependencies, but the bulk of your components still use normal React-like rendering.
[+] [-] valtism|4 years ago|reply
That said, I think that easily the most difficult aspects of react revolve around how re-renders are triggered. Maintaining referential equality to stop unnecessary renders gets tricky when you are passing functions or objects. Suddenly you need to be using `useMemo` and `useCallback` and passing dependency lists that all have to be primitive values unless you want to memoize them as well. It can become such a headache that the official line around it mostly seems to be "make the render fast, don't worry about unnecessary re-renders" – good advice, until you hit a use-case where you need to worry.
Solid takes these problems and just vanishes them. UI state knows what its dependencies are automatically and only updates when they change – even in a sub-component level!
To be fair, I've never used Solid in anger, and moving to it would be a big ask when there is such a good ecosystem built up around react. That said it is easily one of the most exciting projects on my radar, and the developer Ryan Carniato seems extremely knowledgeable in the area.
[+] [-] StevePerkins|4 years ago|reply
> "Ohhh, an OO pattern with a couple of one-liner lifecycle methods is just WAY too much code! Higher likelihood for errors and worse developer experience."
...
> "So instead, I'm going to replace this with a functional pattern, that crams a couple of lifecycle functions into a closure, and is riddled with edge cases and common developer mistakes."
This article perfectly crystalizes why my career has tracked toward the backend over the past decade. All of the virtual ink in this article, and honestly most of the complexity in the field overall... and it seems to really all just boil down to, "I think this looks cooler."
[+] [-] preommr|4 years ago|reply
react isn't about 'hooks', 'jsx', 'top-down-state', or 'component-driven architecture'. All these frameworks are component-based, can have top down state only (or do bottom up in react), can use things like jsx/hooks because it's just syntactic sugar (vue has jsx support).
react is fundamentally about 'inputs changed, render this'. This got rid of a lot of issues with poor code because frameworks had crappy DX (angluar 1 scope nonsense, and overengineered DI concepts), and people were bad at tracking side effects because a lot of people just wanted a search bar with some cool features and not everyone was building sophisticated products.
That setInterval example is fundamentally against what react is, and is basically svelte/vue/(react + mobx).
[+] [-] lhorie|4 years ago|reply
I mean, pretty much all frameworks these days have that fundamental declarative model, react wasn't particularly innovative on that front (e.g. the declarative model already existed in angular, knockout, etc)
What the setInterval example highlights is that newer subsystems in React like useEffect and Suspense are bolted on top of earlier iterations that weren't originally designed to support these kinds of semantics, and the dissonance between API design iterations has become noticeable. This is a pain point that is relatively unique to React.
The growing popularity of Svelte and Solid are largely because their API designs align naturally with how people expect features to work, without people falling into pits of failure like stale closures and incorrectly wired dependencies. React is popular and it puts bread on your table and all, but pretending it doesn't have warts doesn't do anybody any favors.
[+] [-] joeyjojo|4 years ago|reply
[+] [-] rk06|4 years ago|reply
Vue/svelte/solid do not fight against js, hence they do not end up in similar situation
[+] [-] KronisLV|4 years ago|reply
Honestly, the boon of React is just how easy it is to create components, or at least how simple things were back in the day - it is exceedingly composable, moreso than AngularJS, Angular or Vue have been, at least in my experience. In React, your component can fit within a single file, containing simple syntax, especially for when you're making a pure functional component with no side effects or hooks. And even when you need to add something more complicated, you just have a method or two to change, essentially "progressive enhancement" for your code.
Though admittedly state management, or at least our current approaches to it ruin everything with endless boilerplate (Redux, Vuex etc.) to address an issue that may or may not be easier to represent, though some libraries certainly try (MobX comes to mind).
Of course, my experience leads me to agree with the article, in how React in combination of hooks sometimes is problematic, although in my case that was primarily because of render loops and how the stack traces are akin to JDK 8 NullPointerExceptions, where you couldn't see exactly what's causing you problems: https://blog.kronis.dev/everything%20is%20broken/modern-reac...
I'm probably wrong in liking class based components since those have other issues and Vue/Angular both feel a bit less productive in comparison, even if sometimes easier to reason about, with different tradeoffs to them. Maybe i should check out Svelte some day, but i guess it's all just one long slog of finding what works for you and what doesn't, much like it is with back end programming languages or even relational DBMSes.
[+] [-] drewcoo|4 years ago|reply
The actual article acknowledges that when it's read: "React isn’t truly reactive."
And the author also claims to love React and to think it made things better.
You're arguing with phantoms.
[+] [-] seanmcdirmid|4 years ago|reply
[+] [-] ricardobeat|4 years ago|reply
The 'inputs changed, render this' paradigm (and by this I mean reactivity, not the declarative model) has been around for much longer and is exactly what this post is about - React doesn't really do that, since it relies on you to explicitly tell it, via dependency arrays or setState calls, when to re-render. It is not fundamentally different from `.on('change', this.render)` code we were writing back in 2010, just a lot of syntax sugar on top.
That React managed to sell itself so well, while not actually delivering on the reactivity or performance promises, is the surprising part. I'm excited for the future as we finally move on from this era.
[+] [-] pjmlp|4 years ago|reply
[+] [-] jimmont|4 years ago|reply
[+] [-] MiddleMan5|4 years ago|reply
The transition to functional components was to reduce the coupling between abstract functionality and DOM-related lifecycle events.
React hooks are mostly about expressing where and when you want to memoize a value, with the default being not to.
Once you learn what to look out for, and properly designing and review codebases at scale, these trivial issues don't happen all that often. Additionally, being able to specify memoization parameters explicitly brings extra flexibility and some additional design patterns.
[+] [-] mst|4 years ago|reply
Newer frameworks are absolutely better for anybody who isn't sufficiently batshit enough to do that, but much though I enjoy react + mobx (especially react + mobx-state-tree) I've never got to the "I have the core source code in my head and can mentally dry run it as a desk check type operation when debugging" stage with them like I did with early angular so - with the level of jank inherent in its scope nonsense entirely acknowledged - I still occasionally miss it even so.
(this is mostly me being nostalgic, I think, the newer stuff is absolutely better but that was a fun few evenings and for its era damn but I could make that thing sing)
[+] [-] jillesvangurp|4 years ago|reply
I'm having a lot of fun lately using Kotlin-js for example. We use the Fritz2 framework, koin for dependency injection (popular on Android as well for good reasons), and fritz2 relies on kotlin's co-routines and StateFlow for state management. It makes for a surprisingly concise code base. For example, the counter example from the article with that would look something like this:
There's a lot going on here that I can't explain here. But having co-routines means having a proper reactive framework that you use to react to events and update stores, which is where you keep your state. counter.data is a so-called StateFlow; the render function maps updates in that flow to the dom. In the example I have both a button and a co-routine updating the store via a handler lambda function.Using koin here, just means keeping glue code out of places where it doesn't belong. It's technically optional but makes a lot of sense in larger applications. Because components are extension functions on RenderContext, I use a global variable to get to the koin context. That allows me to inject my dependencies into components with a minimum of fuss. Where Fritz2 gets fun is with more complex state using data classes, lenses, validators, routers and a few other things. And they also take care of styled components and they even have a nice component framework that you can use. Not for everyone and there's a bit of overhead in terms of download size. But great if that less of a concern.
[+] [-] unknown|4 years ago|reply
[deleted]
[+] [-] prohobo|4 years ago|reply
Classes may have been more "code" but were clear to understand. I don't dispute that functional components are probably more efficient though.
Anyways, I've never had this problem with React hooks. I've been building complex dynamic UIs for years, and maybe I've grown to get around these kinds of things, but it seems like Solid.js is solving a niche problem at the cost of making hooks even less understandable?
[+] [-] _ezrr|4 years ago|reply
I've tried using bare React in the past (after using Clojurescript), because I wanted my project to be more approachable for outsiders. But I couldn't really handle the (to me, and the author) unnecessary complexity that's added.
I would even say the Reagent version is even simpler than the Solid.js version, because you're using Clojure's Atom API rather than creating read / write functions. For the adventurous hearted I'd definitely recommend giving it a try!
Edit: Someone posted a Reagent counter example on codepen a few days ago: https://codepen.io/Prestance/pen/PoOdZQw
[+] [-] dhucerbin|4 years ago|reply
[+] [-] jorroll|4 years ago|reply
Having used Solidjs for some pet projects, I've come to strongly prefer Solidjs over React. It's an evolution of react, so I've found my existing skills/knowledge transfers. This being said, Solidjs is brand new and the ecosystem is minuscule compared to React. For this reason, I plan to continue using React for the foreseeable future. One of the biggest weaknesses of Solidjs is the lack of a "nextjs" like framework. It appears work is being done in the solid-start[1] repo, but it looks like it's still years away from being fleshed out. I want Solidjs to succeed, but I'm not interested in being an early adopter.
[+] [-] smt88|4 years ago|reply
It's a little disheartening to see that it's 3+ years old and has only had a single significant contributor though[1].
It's impossible to avoid single-contributor projects in the JS world, especially with Node, but the alternatives (React, Vue, Angular, and even Svelte) are orders of magnitude more popular, so it's one area that we can play it safe if we need to.
1. https://github.com/solidjs/solid/graphs/contributors
[+] [-] fatih-erikli|4 years ago|reply
In reality, you will never need to write an auto-incrementing counter :) React gives you a mental framework, you draw a page based on the state in a declarative way. The clever abstraction you make, the less you write the code, so it's a little bit pointless to compare it with an auto-incrementing counter application, in reality has no use case at all.
[+] [-] namuorg|4 years ago|reply
This post helped hooks "click" for me, and once it did, I've absolutely loved them and now thoroughly enjoy writing custom hooks that greatly simplify my code.
[+] [-] llamataboot|4 years ago|reply
the amount of confusing boilerplate I've seen to keep updated and maintained when a JS framework is loading front-end state by making API requests against a backend and then trying to figure out how to keep those in sync when we could just be firing off SSR HTML over the wire and/or very thin events that FE components can subscribe to or emit for literally no gain in functionality is beyond me
even better, just add reactive sprinkles over what you need reactive and do the rest with standard MVC/REST patterns, if most of what you are using react for is glorified forms, you don't need react for that! user your reactive sprinkles of notification toasts, and chat channels...
[+] [-] rezonant|4 years ago|reply
[+] [-] lbj|4 years ago|reply
Example of a full blown React component in Clojurescript:
[+] [-] brendan-csel|4 years ago|reply
Porting most React code to Solid is pretty easy - mostly involves deleting things that are no longer required (useCallback, useRef, etc) and moving prop access into the JSX to enable just those specific attributes to be updated when state changes.
It has come to the point where I really begrudge going back to working in our React code. Unfortunately those apps will still be around for a long time - but we won't be using React for green-fields projects.
[+] [-] simon83|4 years ago|reply
In my current project I'm using Vue with Composition API which gave me the exact same "aha" moment the author of this article had with SolidJS. Vue + Composition API is way more similar to SolidJS in principle than React + Hooks.
[+] [-] dhucerbin|4 years ago|reply
I've been on this quest for a long time, because I like React model. So I had fun with putting observables in my app (kefir and karet from calm-js and my own thing with xstream), I tried custom selectors with redux and all 'like-redux-but-simpler' libraries, I also tried recoil. These solutions can work and worked for my apps, but it felt like I was fighting against React.
Solid was nice. It provided necessary tools to write interactive data visualisations with ease and in performant way. It has quirks but they are manageable - my team also learned and contributed to projects in Solid.
First, the concept of "component functions" that are more like factories. It is not groundbreaking (reagent had this idea long ago) but is quirky. Thankfully, half hour with Solid playground and everybody can see what code is generated and how it works under the hood. It is really predictable but also tangible - you can play with just part of your app in playground if something is unclear.
Second quirk is props object. I understand the trade-off but it trips users (me included) that you can't just treat it as regular object. Sadly, only solution for this is lint rule - yuck. But it is much simpler rule than hook rules - just don't destructure props.
In the end, Solid is great tool for "web apps". Think about dashboards or diagram editors. Cheap components, fine grained reactivity, focused updates yield great performance results without jumping through hoops.
[+] [-] fuzzy2|4 years ago|reply
May I interest you in Angular? It certainly isn’t cool and I really do hate it. But especially since the AoT compiler has been implemented, performance is quite good. There’s templates, which some folks seem to love. Angular keeps in-memory references to all dynamic elements in a template so they can be updated with high efficiency. It has class components. It has a lifecycle method that is called OnInit. So maybe give it a whirl.
[+] [-] egeozcan|4 years ago|reply
Why? Because of the ecosystem! Do I need accessible, headless components? Use React-aria from Adobe! Do I need state management? There are many established ones, I just need to follow their best practices. Everything supports React, every hire speaks React, and it works, not like "just works", but "... works" and, disappointing to the engineer inside me, this is not something I can trade in big projects.
[+] [-] aryehof|4 years ago|reply
[+] [-] torginus|4 years ago|reply