Hacker News new | past | comments | ask | show | jobs | submit login
Solid.js feels like what I always wanted React to be (typeofnan.dev)
548 points by jamghee on March 1, 2022 | hide | past | favorite | 425 comments



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


People always use the setInterval() issue as a footgun. The real footgun is people not reading the documentation for the framework they use, because the exact example is handled in the official react docs [0]. I always advice aspiring react devs to understand why this exact setInterval() doesn't work (and why there is the need for the rule-of-hooks), because it will automatically create an understanding how hooks and react itself works.

[0]: https://reactjs.org/docs/hooks-faq.html


When I started with classes, I hardly had to read the docs, few hours and was good to go. For many developers I worked with at the time this was the case.

React was easy coming from jQuery, Backbone other frameworks at the time.

With effects, i've read the docs many times. I still don't fully understand how it's supposed to work.

I don't seem to get the feeling / abstract concept behind it and it still surprises me at times when it fires / or not.

Too much magic for me. But maybe im getting old :).


I had the opposite experience. I started with classes, and anytime I had to do anything even slightly complicated I had to go running for the docs. The lifecycle methods are byzantine and frequently require not only overriding the method, but checking the values of parameters to determine the proper sequence of operations.

In contrast, useEffect() is "exactly what I want" without having to figure out what combination of lifecycle methods to implement.

I'll grant you that this could have been done differently - I'm not a fan of the "order of calls represents hidden state" mechanism for hooks. It's possible to imagine a different API for class components that provides ergonomics similar to useEffect() and useState(). But hooks are still an improvement over the old class component API.


This is my understanding, but I just use hooks, don't know the implementation details. Pure react functions receive props and render output. Each time the component is rendered (basically a function call), it reads the props and spits out some DOM nodes (simplified). But you loose the ability to have lifecycle events. So they invented a mechanism that keeps some hidden state that's tracked for us behind the scenes, and we can then "hook" into that lifecycle state.

The main issue is that it's not a native language construct, but a mini-dsl sort of so you need to have this state machine in mind even thought it's implicit and hidden from you.

Instead of reading the docs, you might want to search online how to write hooks from scratch. It's one of those concepts that needs to click.


Well if you do not want to read the docs, go with vanilla JS ;) Seriously, no matter what you use, read the docs. Arguing that a library should not require you to read the docs is just nonsense - cause any library makes decisions about abstractions for a reason. If you do not want/need those abstractions, do not use the library blindly. It is always the case that you should now and agree with those, that is why you use the library in the first place. So, read the docs, please.


The post you're replying to says they've read the docs many times.


In their defense, they said "read the docs" not "read the comments"... :D


Ok, but surely it’s a good thing when frameworks are intuitive?


Right there with you. I've been building React apps full time since 2016. I used to convince people to switch to React because of how easy it was to learn and how quick it was to get through the docs. Props, state, setState, done.

I've built some really complex frontends with React. But now here I was, struggling with setInterval, ha.


Same, I just use classes for anything that needs more than a few lines of business logic.


I strongly recommend everyone to read A Complete Guide to use Effect[0] blog post by Dan Abramov. He 3 years ago addressed this exact problem and how useEffect solves it.

[0] https://overreacted.io/a-complete-guide-to-useeffect/


Also in most of these cases, the "natural" way people would prefer to write code for an interval would likely have very unclear semantics, particularly if the interval delay is coming from somewhere else such that its value can change (like component state or props).


> The real footgun is people not reading the documentation for the framework they use

I would argue that the real footgun with hooks is the case where a `useEffect` call inadvertently gets triggered on each render because one of its dependencies, passed through another hook, changes identity on each render. It can get quite maddening, and in many cases you don't notice it until someone points out the resulting performance issue with your app. The solution, of course, is `useMemo` and `useCallback`, but it's not intuitive and it can be easy to miss from time to time in a large project.


Sure there is. Set the interval on mount, clear it on unmount:

  useEffect(() => {
    const interval = setInterval(() => {
      // whatever
    }, 1000)

    return () => {
      clearInterval(interval);
    }
  }, [])


I think I ran into a very similar issue recently too, stale closures right? didMount/willUnmount worked perfectly, but useEffect got screwy very fast, even posted a SO article trying to see the best workaround, nothing great.


My favorite accomplishment with react after not using for some time, was to set a conditional timer for app level event using hooks for a set of tiered conditions which should be ignored unless it passed a threshold.

Took me at half a week at least and I felt like a god after. Then I realized this was all phenomenal waste of time and I decided my next job was going to be cloud and backend focused. Only in the js world can you realize immediately as soon as your done that it's effectively worthless programming hassle.


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


I agree that hooks "can" be messy but in my experience they are quite useful and nice to maintain. That is if the code is written with maintainability in mind - which applies to any code really.

I've been working on a quite complex React codebase for 5 years now - it's not the biggest but still not so small. I was there on day 1. The team that works on it now full time is ~15 devs + 10-15 doing some minor stuff from time to time.

We've started using hooks about 1 year after they introduced them. Before we were very deep into redux and redux-saga. Now most of our codebase uses only hooks for most of the things we did with redux before - there are still some redux but most major parts are migrated to using hooks and contexts. It is probably the best decision we made after switching to TS. Most of our codebase is still very easy to maintain. Last year I went on a paternity leave for 6 months and I was pleasantly surprised how easy it was to adapt back.

Edit: forgot to write about Elm. I like elm a lot -even though I don't agree with how it's managed-. I even organized a local elm meetup :) But I think while it is very good in theory, it's not too easy to scale especially if you plan on hiring more devs. Also the way effects and data is completely separated from the view is not very ideal for bigger projects. You either end up having to touch too many files for a single feature or have incredibly big files.


For us the problem with hooks is that we have a lot of complex state that can be shown in different components. So global stores are a must. But then bugs in the hooks can be hard to debug. Also lack of forceUpdate for hooks causes to use artificial state counters complicating the code. Basically with external state class components are more straightforward to write and easier to maintenance .


Oh that's jumping the gun. TEA doesn’t compose and you can pretend you don't need components until you do and then you end up with big mess of hard-to-follow spaghetti code. It's not 'bad', and has good use cases but The Elm Architecture is not without flaws.


Elm composes just fine and you can get close to the semantics of components. I wrote this over 3 years ago, have been using it in many projects with no concerns. https://package.elm-lang.org/packages/z5h/component-result/l...

There are other ways to achieve similar. You don't end up with "big mess of hard-to-follow spaghetti code", you might end up with a "large amount of unfamiliar-looking code". Getting better at reading a larger-than-anticipated amount of unfamiliar code takes a bit of practice, then it isn't a problem. And then you have all the benefits of Elm with zero problems.

The actual hard thing about code is managing complexity, and working with "it just works" magic when it doesn't "just work".

I agree Elm has some flaws, and there is an initial up-front time investment which is unsavoury to many.


Yeah? I wrote something to deal with it too (https://github.com/toastal/return-optics) 5.5 years ago. You arguably chose the wrong data as `(model, Cmd msg, Maybe extMsg)` instead of `(model, Cmd msg, List extMsg)` which would give you more flexibility and still functions as a monoid on [] instead of Nothing, but allows multiple messages shrug. I tried this approach more recently and it involved me having to encode all of actions in a massive tree and then I still had issues with certain messages including now having to UUID all elements that that previously I didn't need to think about. It was a mess, but the best I could do with the tools at hand.

If you compare this to Halogen (https://github.com/purescript-halogen/purescript-halogen/blo...) where you still have purity but can set up subscribers and listeners from any component. It's much easier to use and for some components like dialogs, it's much simpler. And this actually isn't the best example because with the latest Halogen, Portals (https://github.com/purescript-halogen/purescript-halogen/pul...) was introduced so you can launch a dialog on the spot instead of even needing to communicate between them at all.

This is my anecdote though. And I've been away from Elm long enough now (around 1.5 years) that I've forgotten all the specifics.


Thanks for qualifying wrong with "arguably". I did explain in the docs that it was a design choice, but I see your valid point. My motive was to simplify calling code and require dealing with the external message. It's had the intended effect so far. I'll read over the halogen stuff.


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.


We can at least eliminate ES5 now that IE is dead.


Hooks triggered my code smell detector so bad that I didn't even bother finishing the tutorial.

That said, I still preferred React.createClass so I guess I'm somewhat off the beaten path when it comes to React.


Back when they were initially released my teammate started rewriting everything in them. Little did he know, if you're not careful with hooks, you can severely damage performance.

My take is that if a feature requires a linter to be properly implemented then it's obviously giving the developer too much rope to hang themselves with.

Also it appears that they were introduced mainly for performance, but for some reason sold as the new "better" way of writing applications.

I actively avoid React and its ecosystem, because I believe there's just too much FOMO and "fashion"(for lack of a better word) in what drives its development and not enough meritocracy.


Read the implementation of Hooks (in the React codebase) if you want a big LOL. They're a slower, partial re-implementation of objects with bizarre custom syntax. In a language that's already so heavily OO that the functions are objects.


>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.

The flexibility in its toolchain is nice, however


> "glacable"?


Opps, "glanceable"


Yes hooks are not great. They violate too many functional programming rules that the react team pretend to care about.

I still like using them however. I had a few issues when trying to do more abstract code.


I don’t understand why people think react hooks are meant to be ‘functional’.

They are highly, deliberately, emphatically procedural.

The order they are called matters! The time of when they are called matters! The number of times they are called matters! You get a different result from calling the same hook with the same arguments at different times! You get different results if the call site for them is in different places!

They do enable you to do some things in JavaScript that are more easily enabled with higher ordered function stuff in functional languages, but there is nothing ‘functional’ about how hooks work or how they are used.

And that’s okay! ‘Functional’ doesn’t automatically mean ‘better’ and ‘procedural’ doesn’t mean ‘worse’. For what they do, hooks (considered as the entire hooks ecosystem including linting extensions that help enforce the rules as if they were syntax errors) are a very clever extension within JavaScript syntax that let you pull off some very neat separation of concerns in a reactive code model.

‘Functionalness’ has very little to do with whether hooks are good or bad.


> I don’t understand why people think react hooks are meant to be ‘functional’.

Because when function components first came along that's what the whole of React was supposed to be: "functional" UI, using things like redux or mobx for state. Plenty of guides still use that terminology even though React's homepage now uses "declarative".

That said I think they were always misusing the term, I remember this because I used to complain to people that react itself was declarative, not functional, and just gave the option of writing in a functional style.


I'm not sure what happened over there but they completely lost the plot with hooks. And now you're not "correct" or "modern" if you don't use them. They're ... fine, I guess. Most of the time. But not at all in line with the fairly clean and consistent vision of early React.


>"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.


Read the hooks FAQ, the linter is not wrong, you are :) https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-f...


Did you see my comment about "yes I could rearch my code but..." The fact that I'm writing in some very specific way to please a linter IS the problem... regardless of whether we tell someone beforehand.

If I know I have a dependency I want to ignore, to achieve a certain result. Then I have a way to achieve a particular data flow. One can _say_, that's wrong but it's a deterministic way to achieve that result. Calling it "wrong" delves into the weirdness of hooks. Love them or hate them, they create patterns which must be learned and mastered. And even then... they can still be confusing. Except for on the internet obviously, here we find people who never have problems with them :)


I like Elm. :)

On the JS/TS planet there's https://cycle.js.org , which comes close.

Looks even better than Solid IMHO.


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.


The idea with hooks is a compiler could generate the dependency array. The flaw isn’t with the hooks paradigm


But those dependencies would be static. This goes beyond that. I won't call hooks flawed, they are suitable for React's model, but looking at what reactivity does is a different sort of thing. It is a subtle difference at first.


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.

  [1]: https://www.solidjs.com/examples/todos
  [2]: https://roblox.github.io/roact/advanced/bindings-and-refs/


>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 find this a totally bizarre complaint. I've spent the past few months working on Svelte stuff and I've seen people on HN make this same complaint about Svelte's templating language with {#if} and {#each}. Who cares? What is so wrong, exactly, with "reinventing a concept that's already in the language"? It does not make code any harder to understand or to write, and it does not harm performance (in this case, quite the opposite).

I would much rather have a reactivity model where I plug in completely standard concepts and patterns (a for loop) than one where I have to deal with a bunch of framework-specific, complicated ones (hooks). That Solid's reactivity primitives are familiar is an advantage, not a disadvantage.


Because you sometimes want to filter, sort or project your data. Then you have to handle this in viewmodels or invent more and more features for the templating language. Then you want to refactor into components. So you need facilities for invoking subcomponents. Maybe you want something recursive to display tree-like data.

So you end up with a secondary full featured language usually with worse IDE support, worse error messages, more surprising issues, etc. You need to understand the scoping mecanisms and if things go wrong hope there is a debug tool available.

And in the end those templating languages do not prevent you from mixing UI responsibilities from the rest of your code.

If you want a reactive model you can have one. I personally prefer explicit messages like calling setState.


> Because you sometimes want to filter, sort or project your data.

The idea that this type of thing should be happening anywhere near the view rendering loop is the exact reason I've not had a great time picking up React codebases.

By the time you're rendering data into markup, the data should be in the exact state you need it. No further filtering or data mangling or sorting. That type of data manipulation should happen at the point of data change and then it shouldn't happen again until the data changes again.

The simplistic templating languages in Vue/Svelte/Alpine/whatever-comes-next force you to pull your data manipulation back to somewhere more appropriate, with Vue even throwing a warning if you try to filter within v-for construct.

Because React is JS, people are let loose to do wildly inefficient operations and do them over and over and over whenever _anything_ in that component changes.


I love vue’s concept of computeds. It makes me think back to knockoutjs when things felt like they “just worked” as long as you knew where the ES5 footguns were.

It’s nice to have a concept “ground truth” in data and props and then computeds that sort of tie it all together.


My feelings exactly, it makes for a satisfying separation of concerns, and if you understand what's going on under the hood it makes for cleaner templates and more obvious component code.


Yes, and those are available as well with MobX (in React), Svelte, etc.

You can do same/similar with React hooks, just not as clean or obvious.


You can 100% do these things in React, I don't believe React is a less able framework by any means. If anything it gives you a powerful toolbox and pulls down the guard rails.

I do, however, think that working with React changes your mental model somewhat, and when I'm working with React I catch myself doing a lot more data wrangling close or in the rendering loop than I would in any other modern framework. Certainly since class components have fallen out of favour, you're working with a function designed to be run hundreds of times, while Vue and Svelte both provide clear patterns to deal with data at the point of change, then separately deal with updating the display of that data as required.

It takes using something like MobX to really push a React codebase to a data-driven model and that means many inexperienced developers fall into the common pitfalls far more easily than if they're using an alternative framework imo.


Exactly. The whole paradigm of reactive data binding is that data dictates what the UI should render. Don't solve your data needs when rendering ffs.


> So you end up with a secondary full featured language usually with worse IDE support, worse error messages, more surprising issues, etc.

Gilad Bracha calls these "shadow worlds" https://gbracha.blogspot.com/2014/09/a-domain-of-shadows.htm...


Haven't used solidjs before, but I paid the doc section a quick visit and saw that it's basically <For each={foobar}> where foobar can be whatever javascript code you want. So you can certainly do filter/sort/project on your data before rendering.


You’d probably not want to project inside that loop though, but before it, in a reactive effect.


A react feature that I appreciate is that it is "just javascript". It's easier to learn how to loop or have conditionals in React because it uses native JS features. It makes it easier to understand, for me.

Having templating DSLs in other frameworks isn't a deal breaker, but it's a pro of React that I appreciate.


But I mean, it's not really. A hook invocation looks exactly like a javascript function invocation. Except it's subject to hook rules. Those don't come from javascript. Those are language rules that come from react. And even worse, the syntax for invoking a hook is exactly the same as the syntax for calling a function.


I've not used Svelte, but when I've used such DSLs the problem tends to be that they're not very flexible, and as soon as you step outside of the provided helpers you're stuck and you just can't do the thing.


Do you have an example you've run into where a DSL such as Svelte's or Vue's has actually stopped you from doing something? Would be genuinely interested to see it as I've never run into such a situation myself.


It's true, but the counterpoint is that DSLs make your life much easier in 90% of the cases (if not more).

Personally I've never found a problem I wasn't able to solve in Svelte.


> It does not make code any harder to understand or to write,

Well, what can you tell me about TypeScript type inference for this custom DSL?


I wouldn't call <For> in Solid a custom DSL anymore than any given React component. And Solid looks to have good TS support.

https://www.solidjs.com/docs/latest/api#%3Cfor%3E


Have you used Vue 3? There is fantastic TypeScript support in templates, including comprehensive intellisense for the templates in VS Code with the Volar extension. Surprisingly, _refreshingly,_ good.


> What is so wrong, exactly, with "reinventing a concept that's already in the language"?

Nothing, inherently. Just like there's nothing inherently wrong with having extremely clear and simple rules for how to use hooks, and lint rules to identify when you're not following those rules. Nothing inherently wrong with either, some people just have strong distaste for one or the other.


> 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.

Once you deal with larger amounts of data and need virtualised rather than fully-materialised lists, you start using different things in React as well. The fact of the matter is that if you care about performance at all, the simple ways are just insufficient, and the native language constructs were designed for procedural programming, not reactive interface rendering, which requires fundamentally incompatible semantics. It’s not even fair to claim that React uses regular JavaScript idioms—VDOM, hooks, the entire shebang is all about eschewing regular JavaScript idioms because they don’t scale. (OK, so there’s also the matter of transient state like scroll positions, element focus, and form field values; it’s not fair to say that React does all these things purely for performance’s sake, as the naive immediate mode approach would also break such functionality.)


> virtualised rather than fully-materialised lists

I want to push back somewhat on this practice. Our computers are fast enough now, and the browser implementations optimized enough, that they should be able to handle thousands of materialized list items without breaking a sweat. Sometimes you really need virtualization, e.g. if the underlying data source has millions of records. But if the data can be fully materialized, then the implementation is simpler, and the user can take advantage of things like find in page. Virtualization is a convenient way to avoid the inefficiency of unoptimized VDOM-based rendering (e.g. with React, and yes, I know there are other optimizations available in React), but fine-grained updating (as in Solid) is even better.


A few hundred, sure; a few thousand, it’s starting to get a bit iffy.

It depends a little on the complexity of the rendering for each item, and where you are fetching the data from, but when you’re into thousands of records you’re very likely to need at least some partial rendering. Suppose each record’s data is one kilobyte (I’ve seen far lower and far higher), then one thousand records is already one megabyte, which for many people will take multiple seconds to transfer, so you’ll still want to load the visible records before fetching more, or do streaming parsing of the records as they come in. And that’s ignoring the backend’s performance on fetching records, which must be taken into account too.

People are certainly often too eager to reach for virtualised lists, or worse still lazy loading without reserved scroll height, but even at a thousand records with simple rendering they’re still probably generally warranted—fast computers can cope with comparatively little visible difference, but on slower ones (especially older and cheaper phones) you’ll easily feel the difference. Memory usage can also be a concern for larger quantities of data and DOM (1000 × 100KB = 100MB).

I’m saying all this as one that scorns React and VDOM stuff in general as unnecessary performance overhead, and likes to use Svelte and plain JavaScript and things like that (or better still, to eschew JavaScript); and who worked on Fastmail’s webmail, which certainly uses such progressive-loading lists, on a precise-DOM-updates framework that cares significantly about runtime performance. React and its ilk are certainly particularly prone to using virtualised lists as a crutch to work around their shortcomings.

All that being said: yeah, I wish things like Discourse would stop doing aggressively lazy loading when there aren’t even several hundred comments in a thread. Render just the things on screen to begin with, if you must (though it’d be better to just send real HTML from the server and let the browser take care of all this, even if it complicates your JavaScript loading), but then load all the rest straight away so that I’m not penalised just because I’m on the other side of the world from the server, and let the browser handle in-page search.


Aggressive virtualization, especially if it also involves removing stuff that scrolled out of view, also bogs down some screen readers (particularly Windows ones) that have their own representation of the web page.

Speaking of both Discourse and screen readers, before my stint at Microsoft, I wrote a Windows screen reader, which tried to detect client-side page navigation by watching for the URL (minus the fragment) to change. Discourse's infinite scrolling implementation broke this heuristic, because Discourse would use the history API to update the URL as the user scrolled. Not sure if I or they were in the wrong there.

Interesting that you feel that Discourse penalizes you for being far away from the origin server. When Discourse was new, one of the founders blogged about how their heavy use of client-side JavaScript made the application better for users far away from the origin server:

https://eviltrout.com/2013/01/06/turbolinks-and-the-prague-e...

Maybe the author had a point, but it sounds like Discourse still relies too much on frequent round trips.

Edit to add:

> a precise-DOM-updates framework that cares significantly about runtime performance

That's Overture, right? I wonder how it compares to Svelte and Solid.


What JS framework would you choose to work with big datasets like, e.g. a data grid with half a million rows that should have a "filter as you type" functionality?


Most of the work for this is essentially database tech rather than UI tech. From the UI perspective, you just need to be able to say things like “current query is ‘foo’, and based on my current scroll position I want to render records 32–86” (since 42–76 will be visible on screen, and then we add a few more for good measure to give a small time buffer for retrieving more when you start scrolling), ask the database layer for the required records, and render them as a perfectly normal virtualised/only-partially-materialised list. It’s then up to your database layer to perform the filtering; whether that database runs on the frontend or backend makes no difference, and whether it’s sqlite.js or records.filter(…).slice(…) makes no difference (though their performance characteristics will certainly vary). This can be integrated with the UI framework fairly tightly, but there’s no need for it to be.

For the UI part of it: what I would use would depend on my requirements (is it a list, is it a grid, how is it to be interacted with, &c.) and what was already in use (React, Svelte, plain JavaScript, other). I personally would often be inclined to implement it from scratch, because I’m typically not impressed with most library options (they have a tendency to be heavy, limited, and slower than they need to be) and am familiar with exactly what needs to go into it to make it as perfect as is possible (it’s not a particularly large amount of work, but it is fiddly in places and must be done correctly or it’ll be awful), but that’s not a course of action I would recommend for most developers.


Doesn't matter which framework if you implement it right. Filtering data from the dataset is plain js, and it will be slow. Rendering the data to screen requires a lot of work, such as using offscreen-buffers for smooth scrolling etc. Actually ag-grid does this pretty well, and I've used it for similar in a React app. Now hold your horses, ag-grid is angular 1.x, encapsulated as a component. Another proof that the framework is not important, you need to optimize hell out of it.


Having recently shopped around, and implemented, this kind of data grid: no JS framework actually handles the hard parts, but (more-or-less complete) libraries exist for nearly all of them.

My specific use case is a React application, and I have found it easier to implement a dedicated listener system, than try to fit things into component states.


Isn't that what a backend is for?


You shouldn’t do that kind of work from the rendering thread, and you should use occlusion culling to only render the needed DOM


Hence my question, is there a datagrid for any JS framework that does all the hard stuff for me?


>…much like traditional templating languages, we get a construct like <For> that reinvents a concept that's already in the language.

I'm right there with you, but when React invents a whole markup language inside of JavaScript, it's not in much of a standing to make purity criticisms.


Well you've obviously completely missed the point of JSX then. The "whole markup language" that they invented is literally a line for line transform. Optimisation aside, there's no reason why line 58 of a file with JSX in it won't be line 58 of the transpiled JS file, and read exactly the same. All JSX is is a custom function call syntax.

It's about as pure as you can get while having any sort of html-ish 'templating' whatsoever.

So, I'd say it's exactly in the right place to be making purity criticisms. They've taken the only approach that preserves the integrity of the code and doesn't involve build time magic.


Solid's <For> doesn't rely on any build-time magic, other than the JSX custom function call syntax that is also present in React. If you want, you can even call it like an ordinary function:

    function MyComponent() {
        return For({
            each: [1,2,3,4],
            children: x => <div>{x}</div>,
        })
    )
It's just a function call, and it doesn't even need `React.createElement`. What's more pure than that?


Normal for statement is more pure than that.


Functional purity? `for` can only have side effects. It has no functional purity. It doesn't even have a value.


Imperative purity.


Oh I missed the point on React altogether. After 10 years, I still fail to see why anyone would use it voluntarily.

But coming back to JSX, a custom function call syntax is OK and pure because it has a one to one mapping on line number?

I don't know if there's much point in discussing purity since it's badly defined and mostly in the eyes of the beholder, but it always smelled like one hacky syntactic sugar to me.


That is an utterly bizarre rejoinder. JSX has always been (and afaik still is) optional and extremely thin syntactic sugar. It’s little more than a convenience macro (so that views can look a little more like the markup equivalent).

It has no intrinsic semantics, and maps pretty much directly to actual javascript (which you can write directly or use an alternative helper for — hyperscript being a common one).


> 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.

This argument is silly because it inevitably becomes a pissing contest of who can be most like vanilla JavaScript. In that case, why use JSX? Just write hyperscript calls instead. Why use React Router/React Context helpers? Just wrap your components using vanilla function providers instead. Why use React Hooks, which inevitably look like magic to a JavaScript veteran because the library inherently hides away some global state? I hope you can see what I'm getting at here.


> 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 had/have your bias, but from playing with it I found a couple things:

1) Like React, you can swap out the template feature for a function call (or subcomponent). e.g. instead of

  return (
    <button...>
    ... 
    </button>
    <For each={state.todos}>
    ... 
  );
you can use functions and loops:

  function displayTODOs<T>(todos: T[]): any {
    let arr: any[] = [];
    for(let [i, todo] of todos.entries()) {
      const { done, title } = todo;
      let elem = (/\* JSX \*/);
      arr.push(elem);

    }
    return arr;
  }
  ... 
  return (
    <button ...>
    </button>
    {displayTODOs(state.todos)}   
  );
2) Even with my bias, I must admit I found the `<For...` syntax to be surprisingly easy to read and fast to eye-parse; much more so than other 'templating' (using your term) languages/macros/syntax I've used over the years.


Likewise I simply don't empathise with the author's complaints. Hooks make sense if you think in closures. Hooks are isolated so you can think about them in isolation.

What I like about the React monoculture is that it's one less thing I have to care about. I can focus on the other aspects of my programs, beyond turning JSON into HTML.

I haven't used SolidJS so I'm not going to put it on blast. However, hearing people compare its reactivity model to Knockout JS gives me the heebie-jeebies, because Knockout projects were horrific to reason about (and test) beyond a certain scale.


> we get a construct like <For> that reinvents a concept that's already in the language

Isn't this optional? Can't Solid use regular JSX loops?


https://www.solidjs.com/docs/latest/api#control-flow

For reactive control flow to be performant, we have to control how elements are created. For example, with lists, a simple map is inefficient as it always maps the entire array.

This means helper functions.


This feels like we're trading complexity here for complexity there, and it seems impossible to judge which way is actually "better". I use loops in React all the time but only have used `setInterval` in a component a handful of times..


Most of our jobs is determining the right trade offs.

I don’t see the big deal here. Error boundaries, suspense, context, very popular routing libraries all have used components to encapsulate functionality. That’s to say first party and third party functionality in the React ecosystem have gone down this path.


We are not trading some complexity here for some complexity there. We are trading a huge amount of complexity for a framework that is simpler by an order of magnitude. Simplicity is one of the really undeniable benefits of Solid once you gain a decent understanding of the framework. React at times might appear simple on the surface but the overall complexity is pretty huge compared to Solid.


I believe there’s a finite, fixed amount of complexity in problems; that there is, categorically, no way to solve problems without that amount of complexity that is inherently part of the problem domain.

So, what you have here does not remove complexity from problems, it moves the complexity from one place to another.

So, when you have a simple task, and a straightforward framework, what you see is “it’s easy!”. …because when you use the complex framework you get a bunch of “solutions” to problems that don’t exist on your problem.

That’s why it appears overly complex.

…but for a complex problem, when all you have is a simple framework (like <For…>) you have to implement the complexity yourself, which makes you view the framework as feeble and under whelming.

So, you are just moving the complexity from one place to another; the question is, is the complexity of react really something most people need, or can a framework like solid solve the 90% of simple problems most developers have?

It’s hard to tell.

Most new frameworks excel at solving simple problems because it makes for cute demos.

Is solid any different?

That’s my question. How does it work at scale, for large complex projects? Is there a whole design system implemented in it? Who’s using it and for what?

The claim that it’s “not complex” doesn’t help.

All that means is there are probably a crap load of things it doesn’t include I’ll have to do myself.

There is no magic bullet that removes complexity from tasks.

React is a complex beast, and a nice clean framework to replace it would be welcome.

…but you have to approach this kind of discussion honestly.

Hello world examples are a dime a dozen.


I feel like if it were actually an order of magnitude less complex, it would be eminently obvious from a blog post about it? Maybe I'm just tired and don't "see it" for whatever reason, or maybe I need to find some better examples.

From the moment I saw a post about immer.js, I was sold because it seemed like an obviously better solution for the vast majority of cases where I would otherwise grab Immutable.js, a library that I wanted to like but inevitably struggled against.

This.. isn't quite as revelatory. I'm not saying it's not all that you claim it is, it's just that from a glance, I don't see how this somewhat different approach addresses the problems I run into often with React in a major way (beyond the claimed performance boost).


immer.js can have terrible performance for data structures starting already with 100 of elements. Using JS proxies is not cheap.

We have found that continuing to use immutable.js Map and List but using plain JS objects, not Records is sort-of a sweat spot. But one needs to enforce immutability with Flow/TypeScript read-only types and use the latest immutable.JS to make it work.


My library (see other comments) has components that are about half the amount of code as react or solid and in pure javascript. The javascript shows exactly was is happening, as opposed to the "simplicity" of react that shows a pretend version of what is going on, then 5 years later people discover that is a bit of a problem and now we are onto the next library (solid?).

I wonder if I could have your opinion on my library, and why half the code, native performance is actually more complicated in the long-run. I don't know the answer at the moment. Documentation isn't complete... it is just web components. eg hello world just becomes a function like const component = hello('Andrew'); document.body.append(component); in the example on github. Anyway, the todo shows a more typical real world example I guess.


JSX doesn't have loops. When using React, you use regular not-React-specific Javascript tools to do loops and create lists of React elements.


If this is true, why do you need unique item keys? React cares about loops a lot more than it may seem at a first look.


keys are not specific to loops. They are needed whenever you are returning a react document with a dynamic structure. They allow for more fine grain dom updates.


>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!

Actually, that is only half the reason. You can do whatever you want in my library (github.com/thebinarysearchtree/artwork) and it doesn't have any kind of virtual DOM or whatever Lit does, because you just create elements with JavaScript. The second reason, that everyone just assumes is the default, is that React has to use HTML-like templates and not just JavaScript.


JSX is a purely optional part of React. You can use it with just Javascript and no bundlers just fine.


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.


For the other Americans, “used xyz in anger” means “used xyz in production”.

https://english.stackexchange.com/questions/30939/is-used-in...


Speaking as a british programmer, if a given piece of software hasn't made me yell expletives at the screen at least once, I probably haven't done something sufficiently non-trivial with it to count as "used in production" yet.

But then again even by british standards I am unusually sweary when writing code.


Nope, you get it. Both you and GP are saying the same thing, I think.


I always assumed it was something like "I got so angry with a certain solution that I gave in and tried this other one".


Had to explain "used in anger" to my German gf last week, as her boss had used it and she was confused about why he was angry with her code. It's surprisingly hard to explain the nuances of it.


Weird. I'm not British by I say "used xyz in anger" because I read lots of other programmers saying it. Had no idea it was regional, thought it was hacker lingo like "grok".


I'm not an English native speaker and today I learned that I misinterpreted a lot of what was said to me in the past.


Australian living in England as a dev for 5 years. Never heard of "used in anger" in any context. Weird.


Australian living in Australia for most of my life. I've heard "used in anger" lots of times. I can't remember when, or by whom. But its certainly a thing I've heard people say here.


Serbian living in Kazakhstan as a dad for the last 4 years. Heard it plenty of times before, but never in the context of computer programming. Weird. :)


> thought it was hacker lingo like "grok".

How is grok considered hacker lingo? I would think other people than hackers have read Stranger in a Strange Land.


Thank you! ;-)


> 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!

I haven't looked at Solid but I've used MobX extensively, and this sounds a whole lot like it. It integrates well with React, so you might give it a look if you've got an existing React codebase.


I second that! MobX + React is a good way to scale your codebase, not just performance-wise, but understanding-wise as well!


Agreed, after 6 years of use, my gripes with React come down to the re-rendering and lack of syntactic sugar for commonly used things. Hooks can get messy, but complicated components were complicated even with the class syntax.

What React has going for it, is that it is predictable. That is an extremely important part of any tool.


"UI state knows what its dependencies are automatically and only updates when they change" - you should check out [valtio](https://github.com/pmndrs/valtio).


There are a lot of React state libraries that do similar things with Proxies. I think the part that is not as emphasized is how that reactivity extends to the view. Instead of re-rendering components it uses that knowledge to directly update portions of the DOM. So while MobX, Valtio, Jotai, Recoil etc localize change in React, they still feed into the whole VDOM React cycle, instead of just updating exactly what changes. It's not a characteristic of these libraries but the fact they feed into React.


Solid very much seems like "what would happen if somebody took the way I usually combine react + mobx and built something designed for that from the ground up".

The only thing it doesn't obviously do better is the JSON Patch generation stuff that I get from mobx-state-tree and I use the word obviously because it would not at all to surprise me to discover that solid already does that and I simply didn't RTFM hard enough yet.

The sheer level of ecosystem (and the "nobody ever got fired for" advantage that results) may keep me using react but solid is a bloody impressive piece of kit and whether I ever end up using it myself or not, "bravo, sir" applies.


Though you can use smaller components to minimize the changes. Valtio i feel is one of the simplest of those. But yes they are no way compared to solidjs. Of the numerous frameworks in recent years solidjs has the only which has interested me to pick up and use it. Though still not in production.


Use `reselect`!


The author lost me in the introduction.

> "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've been in the industry since the days of VB6 (which I loved). I've never been more productive than I was with VB6 and WinForms. That said, UI has always been a mess, even then. The more feature-rich you want your UI, the gnarlier and messier it gets.

Everyone always likes to blame the front-end engineers for being slovenly, but my experience is that; UI is just messy. It's just hard to program cleanly.

Not to mention, modern UIs are generally more complex than the old VB days (at least, my programs are): they have to adjust to various screen sizes. They are almost always client/server apps and need to account for more failure modes. They are built on top of a mess of a document layer that was never intended to be an application layer.

This last point is the reason the front-end space has so much churn. It's still an unsolved problem, and may never be well-solved, but I'm glad folks are trying to solve it. React was a step-change for me in terms of building better UIs. I'm looking forward to the next step-change.

Lastly, before someone makes the argument: no; we aren't going to stop building applications on top of the DOM + CSS. Not until someone comes up with an alternative that gives us the same ease of distribution and broad base adoption.

Edit: I should note, I'm a full-stack dev, and have been more back-end than front-end for most of the early part of my career. The backend is always easier for me at least, but I don't think it's because front-end devs are hipsters.


> UI is just messy. It's just hard to program cleanly.

I'm an almost entirely frontend dev (iOS + macOS now, web development in the past) and this is what I've come to believe. It's easy to program UI cleanly if it's extremely basic or not very usable — but the second you want an animated transition between states, or to remember what the user checked on the last page so that you can keep it checked on this page, or any number of other things that you need to do if you actually want people to _enjoy using your software_, it gets much harder.

My take is it's because building UI is building software for humans, and humans often want behavior that doesn't allow for clean abstraction. Backend dev is more about building for other software -- not that it's easier, just a different set of problems.


> My take is it's because building UI is building software for humans, and humans often want behavior that doesn't allow for clean abstraction. Backend dev is more about building for other software -- not that it's easier, just a different set of problems.

Never really thought about it this way but I completely agree


I have been in the industry since punch cards were a thing and servers took up multiple rooms. I've never been more productive. UI has gotten a lot better since those days and I have migrated my efforts to use frameworks such as Vue.js which is sofar the most elegant and modern approach for doing UI work. Creating a desktop app is very easy with electron js although beit a bit heavy and not the most effecient in terms of memory, cpu cycles, etc but it's easier than punch card days I used to enjoy.


Hard to beat Vue, especially when combined with Quasar Framework. The absolute simplest way to deliver SaaS and enterprise apps.


I've also been doing both sides since the 90s and it's funny how people speak of "just" frontend coding. It has lots of async/parallel issues with messy constraints, and it's the dumping ground for dirty data and error handling issues. It's often done poorly, but doing it right is a very complex problem.


Back when I studying CS and as Junior, I would laugh at memes about "Frontend vs Backend" where they described the Frontend as this pretty pasture and the Backend as this god forsaken place of spaghetti code.

In a way I find this meme even funnier now because of how backwards it is. Often times it's the backend that's really clean and organized and the frontend is a hot mess.


I have worked in both kinds of projects. Those where the backend was the mess and those where the frontend was it. Good devs, who take care and are given the time to do it right, can create both in clean manner. I think usually the problem why frontend tends to get messy: There is usually a lot more pressure, unclear goals and instructions involved. And of cause a foundational technology that is already incredibly messy especially if you have a wide browser support goal.


VB6 was great. I also really enjoyed RealBasic for desktop apps on the Mac. It just worked. Network programming with sockets was so simple. Now I open XCode and am completely overwhelmed.


The OO pattern is far worse, and it's easy to show why: there is only one lifecycle method of a type for each class. This means that when you have a set of related behaviors, you have to split them up across several lifecycle methods and keep them in sync. Sure, that's fine if you just have "one-liners", but software grows, and over time your class will become difficult to maintain as each of those lifecycle methods start managing several different behaviors, all interrelated. With hooks, this isn't a problem: each set of behaviors can be encapsulated by a single hook that handles all lifecycle events together. This can be further abstracted into its own function, which allows them to be trivially reused in any other component. It's simply not feasible in OO patterns.

I think if you actually used hooks, you'll notice this immediately. It's not a matter of "looking cooler", a lot of language theory went into making UI development functional and less stateful, which in turn makes it far easier to manage complex applications. A small team can maintain a pretty huge beast if using hooks effectively.


> 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.'

IMO there's significant complexity in building a feature-rich frontend client. The "thicker" the client, the worse it gets. There's definitely a lot of 'I think this looks cooler' going around, but also we shouldn't forget that the need to come up with something better is partially a response to very real, very-not-imagined, frontend complexity.


> IMO there's significant complexity in building a feature-rich frontend client.

Doing complex things is always complex, but when SIMPLE things are complex, then something is going very wrong.

And this is exactly where we are with most JS frameworks these days. Layer upon Layer of abstraction, and instead of the complexity the dev has to deal with decreasing as a result of it being abstracted into frameworks offering simple interfaces, complexity increases.


> this is exactly where we are with most JS frameworks these days

Have you been saying the same thing since 2014? JS frameworks have never been simpler, never been more discoverable thanks to TS, and there are plenty of levels of abstraction a developer can put between themselves and vanilla JS. But even calling React too-far-abstracted-to-be-simple is comical; it's a simple system offering composition of components.


> "I think this looks cooler."

You're using this straw man to demean the author, but I think you miss an interesting point: it can be very hard in software developer to articulate the good and bad. "Uncle Bob" refers to this as "code smells", where you can't quite say immediately what's wrong here, just that you don't like it much. Something smells bad.

And maybe the point of Solid is to point out a mistake React made in their implementation of hooks. Hooks were badly needed, no doubt, but often times an okay solution to a really serious problem can look like a great solution.

The back end went through a lot of this already. That's why the patterns are more stable there, and why you feel less churn. The front end is exciting precisely because there's so much to still figure out!


Super subjective.

To people who know what they are doing more code than necessary to accomplish a task is the code smell.

To people who are super insecure vanity code is required because patterns are memorized, so any deviation from the unnecessary boilerplate is the code smell.


> To people who know what they are doing more code than necessary to accomplish a task is the code smell.

While I agree, I think a lot of developers are oversensitive to boilerplate, sometimes to their own detriment. And the effort to shave off boilerplate quickly runs into diminishing returns. Beyond a certain point you end up with code that is harder to use because of the layers of abstraction you've added to avoid the last-mile developer writing unnecessary boilerplate.

But I am a touch typist, whereas I'm noticing more and more developers who are not, so maybe there's something there.


I didn't find the word "think" in the article. Did you mean "feels like this looks cooler"? Feeling does appear to be the prevailing way decisions are made and put forward in the community. I think that's a problem. And one that won't be going away anytime soon.


When you say it out loud, it all makes sense.

UI developers focus on user interface, and much of that has to do with looks, style, and personal ergonomics. In a way, it makes sense that the engineers steeped in this type of problem set would be oriented to thinking this way. Even if they aren't initially, the probably will be over time.


UI development seems determined to repeat every mistake we've made on the backend over the past thirty years, while adamantly refusing to ask us about any of those mistakes.


I don’t think there’s much overlap between the two, besides general programming best practices.

In my experience, backend development is often simpler because you can move any state out of your own code into dedicated external components, like databases and queues. With frontend code, you have to manage state; there’s nowhere else for it to go.


Care to elaborate? What mistakes are you referring to?


None in particular, it's just a classic jibe from those who dislike UI frameworks because they don't use and thus don't understand them


True but backends are not always refuge. The back end Java version at my company would have a DI container, a few interfaces, factories, configuration files, custom attributes and a few random library dependencies thrown in.


A factor of two reduction in LOC and an elimination of almost all boilerplate is justifiable in itself. I don't know where the author said "I think this looks cooler"


Seems a bit odd to call the version that repeatedly calls the constructor an OO pattern.


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.


> react wasn't particularly innovative on that front (e.g. the declarative model already existed in angular, knockout, etc)

React was absolutely a breath of fresh air when it was released.

Knockout was similar to Solid.js in that they both have functions that you call which then log a data dependency, then when the data changes the UI updates. This led to lots of pain, because instead of a plain value, you have functions which return values, and you need to be very careful about when those functions are called, otherwise the data dependency might not be tracked properly.

Angular had a similar issue, as its state-based observation relied on special scopes. Updates in the wrong scope could be lost or delayed.

React’s approach of only diffing the rendered UI rather than trying to drive updates based on diffs of the input data was vastly simpler, it was much easier to understand the data flow through explicit state and props.


We also had ractive.js, we had mithril, intercooler.js (now htmx), we even had (fab).js as far back as 2010 with functional rendering. A 'breath of fresh air' pretty much depends on what you had been breathing.


React wasn't exactly innovative as such so much as it was a very carefully designed (I don't mean they didn't make mistakes, mind, only that they clearly put effort into making new and exciting mistakes) implementation of the model that successfully broke into the mainstream.

The phrase "an idea whose time had come" springs to mind.

(this comment is intended to read as professional respect, not fanboying - the extent to which I succeeded in that intension must inevitably be left as an exercise for the reader)


> they clearly put effort into making new and exciting mistakes

Very good.


React also requires that you call functions to trigger rendering (be it via setState or `[, setFoo] = useState()`) and it definitely has warts that trip people up too (e.g. semantics of calling setState twice synchronously). The dependencies array in useEffect is also conceptually the same mechanism you claim causes pain in Knockout.

IMHO, historically, the bigger pain point with the reactive model was data marshaling/unmarshaling (e.g. updating some subtree of data and then needing to send the root of the data tree to the server while maintaining reactive bindings across a large app and being careful not to fragment source of truth). Ironically, React can also end up in this predicament, because the encapsulation model of its `state` mechanism means extracting the actual state of the component tree is non trivial unless you're using a third party state lib to avoid it altogether in the first place, or at least use useReducer, which is a relatively new addition to React (and even then, it's kinda jank).

These days, React is a hodge podge of many different implementation approaches. Yes, there are props, but Context also exists - and is used extensively in the wild - precisely because props get clunky, and then there's data diffing happening to support `memo`, on top of the virtual dom change tracking. Suspense basically requires your code to adhere to semantic restrictions, i.e. you're not even in control of when your component function is called, which leads to having to tip-toe around that scope w/ extra closures, which in turn leads to all the issues that the article touches on.

The hodge podge issue isn't specific to React; Vue is also seeing pain points from having so many ways of doing things now that they're trying to push a v3 and realizing ecosystems tend to slog.

To your point, yes React was relatively simple when it came out, but as I mentioned, it wasn't the first to take a stab at the declarative model, nor the simplest. It just benefited greatly from the popularity wave of the golden years of Facebook OSS engineering. And from a practical perspective, it doesn't really matter what React was. Idiomatic usage is a thing, and React development today isn't like React development 7 years ago.


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.


In my personal opinion when there is a lot of complicated state in a component and there is no real benefit to splitting it up into smaller components then hooks are inferior to the older lifecycle methods (in creating understandable, maintainable code), however in my experience whenever I come to place nowadays this would be considered heresy and everything needs to be in hooks even if you have 10+ and growing number of hooks to make everything hang together.


Maybe, but in my experience whenever I encounter an incomprehensible mess of hooks it usually ends up because devs were not using all the tools that react provides.

For example a flurry of setStates could be wrapped up in one single state. If it gets too complex - into a reducer.

Components that don’t benefit much from splitting up could have their business logic wrapped into a context, and let the view code be just jsx without all the interweaving of code and templates.

Maybe the one benefit of classes was like it forced you to think in business logic, then render. React still has that, you just need to dive a bit deeper into its toolbox.

The result usually turns out much more flexible - contexts neatly wrap business logic for all of its descendants, classes don’t.

I think this was maybe because react actually allows you to write messy code, and it’s still performant and works. But in the end it just kinda postpones the inevitable maintenance burden.

I guess solid.js from the looks of it might postpone it a bit more. I just worry that solid looks more like magic, and some invariant somewhere will just break and I wouldn’t know what sequence of reactions actually led to that infinite loop that crashed the page. Haven’t tried it myself though, might more understandable in the end…


> For example a flurry of setStates could be wrapped up in one single state. If it gets too complex - into a reducer

You've just deoptimized your app, and your whole component will re-render on every change.

> Components that don’t benefit much from splitting up could have their business logic wrapped into a context

You did it again. More unnecessary re-renders.

> it’s still performant and works

True that 'it works', but it's usually not as performant as you think - we just have really fast computers and phones now.


>You've just deoptimized your app, and your whole component will re-render on every change.

If you have 3 useState each of those will use 3 useReducer internally (every hook is implemented on top of useReducer), If you consolidate them into one useReducer then you will end up with the same thing performance wise. Maybe even better. Whenever an event is pushed into the hook's queue it marks the component as dirty. The next call to useReducer will then reduce all unprocessed events into the current state. It's entirely possible that having less hooks and therefore less metadata in the background can improve performance more than avoiding the theoretical cost of rerendering a component that most likely would have to be rerendered anyway.


Not all hooks are implemented in terms of reducers. `useMemo`, `useCallback`, `useRef` for example are not based on reducers, (or effects). Obviously the effect hooks are based on effects not reducers, and some like ` useDeferredValue` are based on both.

But you certainly are correct that useState is reducer based. I'm pretty sure one is only avoiding rerender via using multiple `useState` if they don't implement the reducer in a way where it returns the original object when there was no net change. If you are able to implement the such that it only returns a new object when there really is a change, then a single useReducer call is strictly more efficient than multiple useStates. (This might require more complicated code, as returning a new object every time is often the easy way to implement reducers.)


Why is performance always argument No. 1? The true rule is, code must be readable and maintainable first. THEN, if there are performance issue with the code (no theoretical ones, you HAVE to have a real-world profiling report of YOUR code in your hands when you argue about performance), you can refactor for performance.


A thing I remind myself of regularly: Avoiding worrying about optimisation up front too much is not avoiding caring about optimisation at all, it's reserving the number of hours you can expend on optimisation until you have enough working to have a profile available, because you are inevitably going to be wrong about which part is actually slow until that point so your optimisation hours budget will be better spent with a profile in hand.

There's a parallel here to "no, you did not find a bug in the compiler". Yes, ok, once every five years or so I actually did find a bug in the compiler, but assuming you aren't that smart is still a far far better default approach.


That's a straw man argument - I did not say it's concern #1, just pointing out that React forces that specific trade-off. Balancing complexity, maintainability, AND performance is really hard with hooks, the lack of built-in reactivity and inefficient baseline behaviour. It's rarely a matter of "just combine it into one reducer".

On top of that, profiling and optimizing a real world application with a dozen hooks in every component is pretty painful.


“Long lists of generic hooks” is a symptom of devs not having climbed the difficulty curve a bit further, to where they are writing custom hooks, and using fewer in each component, IMO.

It’s understandable to stumble into this difficulty though, and a bit of deceptive marketing on the part of the hooks folks. They should be upfront that using hooks well in a real app requires a lot of careful thinking of the kind that many “typical” programmers do not have much practice in, and then the maintenance of whatever code comes from the effort. The functional-programming-mindshare situation seems to be improving slowly, but still.


hooks are inferior to the older lifecycle methods (in creating understandable, maintainable code)

If the lifecycle methods you're referring to are things like componentWillReceiveProps or getDerivedStateFromProps then the React blog covers why they were problematic https://reactjs.org/blog/2018/06/07/you-probably-dont-need-d.... It was very common for developers to make things that would repeatedly rerender when other parts of their app updated. Hooks make that far less likely to happen.

That said, I agree that a getDerivedStateFromProps method is more readable and much clearer than useEffect(()=>{ // stuff }, [big, list, of, props]);


that a particular way of doing things was problematic for the community as a whole when measured across all usages does not invalidate the observation that there were some usages in which that particular way seemed better.

when, as I often encounter, organizations mandate all hooks all the time they are not throwing the baby out with the bathwater, but they are maybe throwing out the baby's rubber duckie without considering that might be useful to have around at times.


When you have multiple hooks in a component it usually makes sense to put them in a function with a good name - a custom hook. It's easy and almost always usually gives simple code.


sure, and sometimes it still would be simpler with the older lifecycle methods. Not saying it is always the case but for some reason it is assumed it could never be the case.


a benefit of custom hooks is that you can test complex state logic isolated from the view


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


React lets you build DOM with normal JavaScript loops and .map; solid requires its own For element. How do you define “does not fight against js”? Because the above feels likes solid fighting against js.


Fully agree.

With React, "plain JS" works just like I expect it to. If I have an event handler that does `x = foo` then I don't expect my component to re-render. Why should it? I'm just changing the value of a local variable. If I want to re-render, there's no way to express that in plain JS, so I'll use React's API and write `setX(foo)` instead. Now it's clear that it's not just changing the value of a local variable, but using React's API to do something else.

With Svelte, writing `x = foo` doesn't just change the value of a local variable. Maybe it's going to run a bunch of magic to update a piece of the DOM instead. Or maybe I need to prefix it with `$: ` which in plain JS is a label for a continue or break statement, but here doesn't mean that at all and means reactivity instead.

Oh, and to conditionally render an element, instead of writing idiomatic JS like `condition && <Element />`, I now have to write `{#if condition} <Element /> {/if}`.

WTF. What looks like plain JS is now magic, and what should look like plain JS such as conditional rendering and lists are now some contrived templating constructs.

And almost everyone here is telling me that I should find that simpler somehow.

It makes zero sense.


Sorry for the "+1" chain but I want to express my agreement as well.

Especially when I compare it to Svelte and Vue (not solid, solid.js is just... good), if I look at a component, with react it's way easier to say what's going to happen.

Aren't there gotchas? Yes, but sometimes it's just stuff JS lacks, and so does React.

I'm very excited about Records and Tuples in JS to help with the immutability, for example: https://github.com/tc39/proposal-record-tuple


Don't forget about having to pass key to each element in React. The simplicity of using map() is an illusion.

SolidJS splits it between <For> and <Index>. <For> is equivalent to passing the object as the key and <Index> is equivalent to passing the index as the key.

https://www.solidjs.com/tutorial/flow_for https://www.solidjs.com/tutorial/flow_index


> Don't forget about having to pass key to each element in React. The simplicity of using map() is an illusion.

That's one of the design decisions I don't fully understand. It knows that there should be a key there so why just not put it there silently and let me override it when I need, instead of screaming at me when I omit it.


The purpose is well-documented here: https://reactjs.org/docs/reconciliation.html#recursing-on-ch...

And it also goes into why index is a poor key (it's basically the same behavior as with no key).

Using object identity to detect inserts doesn't work either, because the map function is returning new React element objects on each render.

Practically speaking if you know your items won't change, then omitting the key or using index is fine.


So SolidJS has this solved elegantly? It lets you choose between object identity and index, and it doesn't have that caveat for object identity because it knows what's going into <For> and not just what's coming out of it.

Don't omit the key in React unless you want warnings.


I know, I've read it. I'm still not convinced. React could just as well generate and insert missing key on build instead of asking me to do this:

... you can add a new ID property to your model or hash some parts of the content to generate a key.

It is just plain wrong to ask me to change my data model because of this. This is part of the housekeeping that I expect the framework -- pardon, library -- to take care of for me.


It can't. Not in a consistent manner. When diffing user provided immutable data you need a user provided key. Otherwise it can't tell the difference between a new list entry and a nested update. You could treat every nested update as a new item but that is incredibly wasteful as it throws away all descendants. This is something all non-fine-grained rendering libraries have to deal with be it React, Vue, Svelte, or Lit.


How does Solid avoid the need for user-provided keys? I thought it also had to diff the underlying array. Does it check object identity?


It does. Just referential check. Our reactivity is nested and we don't want blow out everything so even though there is read/write segregation and immutable interfaces the internals are mutable. In so sorting looks at referential equality, and nested updates don't even trigger list diffing.

Now this does require special process for intaking immutable or big data snapshots where we can't do reference comparison. So we do have a data diffing capability in our nested reactive stores to propagate only what changes. But for the most part common actions like partial updates highly optimized. As well as simple list operations like sorting.


It’s a performance pitfall. The warning is trying to make sure the dev correctly tells it how to and when not to re-render.

Could it silence the warning? Yes. However, the React devs choose not to. I think it’s the correct default, but likely needs a more educational warning message.


i'm finding a hard time articulating what you said, if React is against side effects then useEffect wouldn't have existed and we wouldn't have data fetching.

React is unique in that everything in the component is within the render path, while the rest of the frameworks (that you've mentioned) doesn't.

you might be mistaking "side effects during render is bad" for "side effects is bad", the two statements are not the same.


I mean side effects from a functional point of view. Let me explain in more details

React’s philosophy is View = F(data)

I.e. view is a pure function of data. By “pure”, we mean F() does not do console.log, ajax calls, date time and other stuff which is not consistent every where.

This assumption is ingrained in React. You see it when you are told that react can overrender and your code should handle overrendering. And react works well when user code is pure. However, real world does not work that way. So, there is a need to handle side effects, so that “side effects” works well with pure function assumption of react. This “handling of side effects” is the over head introduced by react.

Other js frameworks (svelte, solid, vue) do not assume full immutability or pure functions, hence User code does not need special handling for these cases


> View = F(data)

That's just how templates are meant to work. Underscore.js templates don't allow making an AJAX call before calling render() either. https://underscorejs.org/#template


Consider following scenario:

User is entering an account register form. When the user has entered a value in nickname, your app must check if it is in use and display error message if the nickname is already in use.

Sounds good, and ubiquitous right? Well, that also require an ajax call to server for validation, so it breaks the pure function assumption right there. Now, you what do you do?


The template's render function doesn't make the AJAX call - not in React, and not in Underscore templates.

When you display the error message, yes, React provides a way to do that without re-rendering the input box and while keeping the contents, but many react devs are skipping the traditional React way of doing that and using react-hook-form instead.

I get what you're saying about the component's data updating the state of the DOM in a way that resembles pure functions, but I think AngularJS did it before React, and that Backbone was not far from this vision. Certainly there are a zillion JavaScript frameworks that do this now, and only React has you jumping through silly hoops like "className" and "htmlFor". https://preactjs.com/guide/v10/differences-to-react/#raw-htm... https://www.solidjs.com/tutorial/bindings_classlist


> so it breaks the pure function assumption right there

There's no pure function assumption being broken here. React is a framework for rendering UI from state and coordinating updates to that state. That's why we have things like `useEffect`, contexts, and so on. The only part of React that is expected to be free of side effects is rendering.

Put another way, given your example, React just says that you shouldn't issue your Ajax call in your rendering code. Instead, you should do it in response to an appropriate action, such as a change event on your form controls.


I fail to see the purity in react with all that useState, which obviously by the name is tracking state outside the scope of the function(eg: it's actually View = F() and data is coming from outside when calling useState). It's not pure at all. I really would like to see something more akin to Elm, where all your state is explicitly passed into the component, and you have commands that update state and trigger the view again, making it actually "functional". I guess this takes the form of redux in react, but I'd like to see it ingrained in the components themselves.


Yes it's var View = F() and var hidden_global_variables_for_hooks;


> React’s philosophy is View = F(data)

Yeah internal component state throws all of that out the window.

That's lit-htmls philosophy, not react. React is all about bundling the view and state and then marathon profiling sessions to figure out why everything is re-rendering all the time.


> 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.


Zustand has solved the complexity of state management for me, although getting {employer} to adopt it is of course a different problem altogether.

It's really bizarre to me how poorly useContext works, in contrast to how good everything else in React is for the most part. Having a good, "official" global state management solution that requires little boilerplate would be a huge benefit.


I am currently most fond of mobx / mobx-state-tree which is a substantially different style but I am very much impressed by zustand as "take the redux style and do it really well".

There are enough different takes on how to state management that I am ... ambivalent ... about whether having a single official solution would in practice be better than the current situation. It's inevitably going to be a trade off and the react team not wanting to pull the trigger on such a thing until they're -really- sure is probably a good thing overall.

As a side note: I agree that useContext is weird, but jamming what (at least according to the mental model I use when working with it) is dynamic scoping into a language without native dynamic scope is probably always going to be at least somewhat weird.


Wait till you realize Zustand does not work on React 18's Concurrent Mode and istead must use Jotai (same author) ?


Zustand 4 already works with Concurrent Mode. Still in beta, but so is React 18.


I enjoy React+MobX with class-based components, because they are nearly as nice as Seaside was, back in the day!


> In React, your component can fit within a single file

Furthermore, a lambda local to a function can be a full-blown component (visible in DevTools etc.).

So if you need a component that is used in only one other component, you can neatly encapsulate it and make it invisible from the outside, which can be useful on occasion.


> react is fundamentally about 'inputs changed, render this'.

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.


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.


Hrm.

"React as Schelling Point" ?


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.


I recently started to look at native web components (custom elements), at first I thought I could replace {insert your framework here} with it, but it seems that it doesn't solve the issue, you still need some kind of framework built on top of it to achieve the same goal.

If you have some recommendations or want to share your experience with working only with web standards I want to read them :)


Easy, Java and .NET SSR frameworks (which support components for two decades now), + vanilaJS for Ajax like behaviours.

Loads fast, easy to debug without tons of layers in the middle.

And yes, I also do manually make use of script and vendor JS libraries.

Naturally it only works when doing side gigs on my own, on big Web projects where my role is mostly BE/DevSecOps, I go with the flow.


That isn't a solution. Everybody moved away from this approach for web applications for good reasons.


The craziness of SPA frameworks running headless browsers, rediscovering SSR as if they are inventing something that no one else thought about, proves otherwise.


Having been on this ride since before HTML was a thing... I'm going to make a prediction: JS SSR will be a brief flash in the pan. In another ten years, we're going to look at it the same way we look at JSF today. "What were we thinking?"


Given the WebAssembly adoption rate of frameworks like Blazor, plenty of people miss JSF and WebForms today.


I agree, I am writing some apps like that and we still want some minimal level of interactivity that is suited to react. E.g. a file uploader.


Well, I think when they moved away, they also increased the complexity of their applications as well.


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?


That is a big call regarding web components. I did some looking into web components for a recent project and React is just way ahead in many areas. You could use a framework like Lit to help smooth things over, but that just speaks to the underlying standard being somewhat cumbersome to use. I totally want web components to succeed and be the new way of doing things, but I get the feeling that is still a while away.


I think calling web components "cumbersome" belies the intention of the APIs, which is to enable custom HTML elements, encapsulation, and interop - fairly low-level concerns.

Lit takes care of templating and reactivity. Web components don't have those, it's expected that you use other methods, including what you already use, to create DOM and react to state changes.

The DOM may eventually add templating and reactivity, but that's a pretty big question given how many approaches and syntaxes there are. Until then libraries are fine and allow for multiple opinions.


Only a clarifying point to add about Lit: tagged templates provide templating and have native support, all Lit does is provide API extensions for reactivity, ie state management. So when a portion of a template is associated with an object, only that part is updated due to how the libraries work. And this is fundamentally all the it provides (unless I've misunderstood something). Lit-html provides functional state+template views while LitElement provides the OO/class oriented lifecycle approach--collectively now rebranded as "Lit". https://lit.dev/ Please call out corrections as needed.


Big call? I suppose there's a point where having done enough work in projects affords some perspective. Whenever anyone gets the feeling to do something else the docs will probably be there, as they were the past few years.


I've been maintaining a web component library with Lit for a while. Web components overall don't feel ready for primetime. Just making a custom input field and have it work with a <form> is chore.

Just look here at how much overall code is needed to do it right: https://github.com/ing-bank/lion/blob/master/packages/input/...

After you get through all the inheritance and mixins it's thousands of lines


I only have this generalized experience to respond with (at the moment): For the private project i work on full-time, the few dependencies related to ING and Lion were the most problematic and the first to get ripped out. I have no idea about the specifics of your dependency, and will not review the work you're mentioning in detail. However, ING, based on my limited experience, is nothing to base any assumptions on. Based on my limited experience, ING and Lion appear to be distinctly inferior examples of work done with Lit and web components. That was my first and last time working with anything Lion and ING related.


That's just one example, and the issues with form associated components are not limited to ING. The Microsoft FAST form components also have a fuck ton of code to achieve what <label> + <input> can do in any non-webcomponent framework.

There are proposals for this and most of the other issues I have with web components. But they all feel like issues that could have been covered from the beginning.

Also if you go all Lit on an app with many nested shadow doms it becomes fairly painful to test with tools like cypress.


What is your point--or question? Form elements are complex. I'm not familiar with cypress. Puppeteer has worked fine for my needs. If the thesis is simply that using web components is a chore, well, welcome to frontend work. It has always been a chore in one way or another. Web components are the first time I can say--for my own professional experience--it was less so than previously. Generally a superior experience to all of: React, Angular, Backbone, jQuery, etc. Maybe you just need to hire me? I couldn't say without more detail.


> That products survive this long with React illuminates how resistant the community is to... reading and doing work in the easiest, pragmatic way possible.

My point is that you were entirely wrong. Web Components are not ready for primetime, they are half baked. If they were the easiest and most pragmatic way possible to build web applications people would do so.

The only thing they are the easiest and most pragmatic for is to build a simple non-form associated component that can be used across frameworks. Like a card.

> It has always been a chore in one way or another.

It's actually never been a chore to make an <input> behave properly. Something that literally every component library needs to do.


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.


What are you talking about? The setInterval example is completely idiomatic React hooks. It is literally given in the React docs:

https://reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-...


Reading it now, the language I used was inappropriate. You're correct, the stated problem is common with registering and triggering self-repeating and timed loops.

That was really my point, don't do that.


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.


The way mobx-state-tree uses generators as a coroutine-like affordance is worthy of study.

I would not at all be surprised or troubled if, having studied it, you still dislike it, but there are definitely ideas in there that I consider to be at worst -interesting-.


>GlobalScope.launch { ... }

I can't begin to express how sad that makes me. Your component should have its own scope that it destroys when it gets destroyed, otherwise your coroutine leaks to the outside world when your component is gone from view.


Sure, but I wasn't going to fit a whole lot of boiler plate in a comment on HN to do that. Using Globalscope in a demo is perfectly valid; a lot of co-routine presentations do that as well. Normally, I'd use some named scope and life cycles associated with e.g. koin modules or a web worker. This is just something that I quickly knocked out that actually works. Also, this is not Android. There are not a whole lot of life cycle events beyond "page loaded".


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?


They literally don't. For some reason the developers think that storing state in hidden global variables (linked list indexed by hook call order) is a good idea. You know, they could have at least defined useReducer inside the class. Of course that would sort of break the pretentious ability to define hooks as global functions that access component state. After all, you would have to pass the current component as the first parameter, imagine the horror!


This reminds me a lot of Clojurescript's Reagent https://reagent-project.github.io/ (link also has a counter example)

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


Imagine if we could have runtime characteristics of solid, dsl of hiccup and simple semantics of clojure atoms and refs!


It should be possible to build an idiomatic cljs layer over solid. Solid’s compilation step can be implemented with clojure macros.


I am already far too nerd sniped by other things to try building it myself but that does absolutely sound like fun.


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.

  [1]: https://github.com/solidjs/solid-start


I think a challenge for new javascript libraries/frameworks is that, for many developers (including myself), we're evaluating the whole architecture. The question isn't, "Which library has the best API and is most performant for rendering components?" It's which choice gets me to "usable app" quickest and most pain-free? Included in the calculation is the ecosystem, the build tools, the documentation, the deployment strategy.

For all these reasons, while I do really love Solid's API, React + Nextjs + Vercel (or another React stack like Gatsby, etc) ultimately provides a smoother development experience for the time being. It isn't enough to build a better React, someone needs to provide an easy to use build and deployment process for it as well.

I ended up giving up on my Solidjs experiments because I spent too much time debugging the build process and porting React libraries. It's still not obvious to me how I could deploy a Solidjs app to, e.g., a Cloudflare Worker and provide a `/api` callable functions endpoint for the application. I have no doubt that I could figure all of it out, but I'm not interested in spending the significant amount of time necessary to do so. I love the fact that Nextjs just gives me all of this. All of this is to say that, while the core Solidjs library is really "solid" (pun intended), I still don't think Solidjs is ready for new projects (unless you really like doing things from scratch).


>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.

The chicken-and-egg ecosystem problem for new frameworks is tough. I've been working on Svelte stuff lately which has a similar problem but less extreme--the ecosystem is still much worse than React's, unsurprisingly, but it's also much better than Solid's right now.

I think Solid's primary branding is around performance and Svelte's primary branding is around it being easy. For getting things off the ground, I think "easy" is a much more successful approach.


Solid has done some branding around performance but the promotion is shifting to be more balanced, as Solid really isn't about performance. Solid's biggest priority has been to give the best DX for building performant applications that stay maintainable at scale and after years of work on the same project. Solid might seem harder than Svelte or Vue to get started with (although this is arguable IMO) but due to it's simplicity I think that it's much easier to master and understand what actually is going on.

Compare this to Svelte which has the goal of creating the perfect high level abstraction so that you never need to understand how things work and was originally created for smaller one off projects with much smaller complexity and no maintenance burden.


This is where I’m at, too — it’s faster and simpler than React, and less magical than Svelte, but it’s just too immature. As another commenter pointed out, there’s basically one contributor: https://github.com/solidjs/solid/graphs/contributors


> 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.

I don't particularly like React, but this strikes me as the one thing it got right; the only "always correct" thing to do is to rebuild the VDOM on any change, so that's the default.

Then you can be more selective about which parts as performance dictates.


That would be true if all a react component was dom output. But since it has side effects (rest calls, mutating state, effects), rerunning everything is the wrong thing to do in almost all instances.


Sure, if you put side effects in your react components, you're gonna have a bad time <insert South Park meme>.


Interesting. I guess it just boils down to different preferences.


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.

1. https://github.com/solidjs/solid/graphs/contributors


I'd also look at other repos. Admittedly for the core code it has been mostly me. I think there is an intimidation factor. When you create a library this performance oriented it is hard to get people comfortable working on the core.

But things like the site, docs etc.. are much more contributors making more substantial submissions: https://github.com/solidjs/solid-site/graphs/contributors https://github.com/solidjs/solid-docs/graphs/contributors

We would have never gotten the docs translated into 15 languages otherwise. I do agree that one should be cautious regardless. But I don't want to underplay the contributions of many contributors putting in improvements every day.


There are projects of mine where I'm the only relevant core contributor but the ecosystem is developed by many other people and it's largely worked out fine.

I would hope that if either of us got hit by the proverbial bus that people involved in the ecosystem would pour themselves a strong drink and dig in to the necessary maintenance anyway.

Projects I've ended up moving on from have regularly worked out that way, and I think that while solid might not be as popular as some people would want for something to bet their production code on, it does seem to me that it's popular -enough- that I don't believe you're a truly dangerous single point of failure here.

(if this comment read as negative rather than an attempt at a clear eyed analysis, I apologise for phrasing it wrong)


I have no idea if it's in the creator's interest but I'd love to see Solid get backed by a company the way React, Angular, and Svelte are. That might make people take it more seriously as an option but, as-is, it certainly feels like a risky option to use professionally.


FWIW, the author joined the Marko core team at eBay a while back.

But from what I hear, he's still pretty responsive on Github.


Solid is sponsored by Vercel, Cloudflare and Netify


solid.js may only have a single contributor but i believe it’s been battle-tested and used in some larger projects (don’t have any examples off the top of my head though)


"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...


What about apps that have no backend? What about apps that should work both online and offline?


^ What about Figma? Photoshop? Not everything is only a CRUD app.


I'm not saying there are no use cases for a JS framework, and I have a few I like, just that the switch to rails api only/separate react app has in almost every case I've seen added tons of complexity without a clear win


Imagine you have a Rails controller with a form template and you want to have a section of the form show or hide based on the setting of other form elements, obviously without reloading. Can you do it? Yes. Does Hotwire/Stimulus make this possible? Yes. Can it be done in a nice easy to use way? Yes. If you stretch beyond what Hotwire/Stimulus can let you do, do you have to completely switch over to the "frontend engineering" path of single page applications (React/Angular/Vue/Svelte/etc)? Also yes. Are the primitives you use in Hotwire/Stimulus in any way connected to the ones you'd use if the use case got even a little more complex? Absolutely not. Why not just have one set of primitives that work for all applications?

It's not like having some frontend templating logic or reactive forms or whatever is going to actually be harder than just doing it in Rails if you are familiar and comfortable with both. If you aren't comfortable with both, then that's fine.


Those are very different applications and the trade-offs involved are completely different.

In the -common- case OP seems to me to have a valid point.


For forms-over-data business applications, sure it's fine. It's tradeoffs around the system use cases, potential/realized functionality, and team.

But I think that serving to public users with server-driven MVC for an application that goes beyond a pure content app has immediate and obvious limits in terms of what can practically done, and the more you try to overcome those limits the more you simply rebuild what is already available in the SPA side of things.

It's also inherently monolithic, meaning that if you want or need to support a mobile/native app for your public-facing app, you'll now need to develop a new "interface" (an API), when you should have already done that to enable the web app in the first place. Is it really worth skipping API/UI separation on day 1 when you know it's going to be needed on week 2?

You might say, it's fine, we're going to just make API calls for the Javascript, but then you've got an inconsistent availability of the functionality, and the second you hand that to another team to consume you will have wished you had simply built out all of the needed functionality directly in the API anyway.


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])))


Reminds me how vanilla hibernate is unusable but Grails'/Groovy's GORM (built on Hibernate) is absolutely fantastic.


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.


As somebody who's quite enjoyed mobx I would encourage you to write up your experiences.

I would hope that while -me- reading any such blog post is almost certainly going to be irrelevant, it might pay off enough in recruiting/marketing/pure nerdery to be worth the effort to write it up.


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.


Pure function components in React with MobX for state management rather than the hooks system has worked out quite well for me.

I do, however, keep finding myself wondering about jumping to Vue and VueX instead.

Ask me in five years, I guess.


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've been using Angular in production for 5+ years. There was a year where things were confusing, state updating all over the place, and general frustration. But once I grokked pure components, and embraced "data down, actions up", it all clicked and just made sense [0]. I find React obtuse in comparison.

Angular front-end, Nestjs backend has fast become my stack of choice. It greatly minimises context switching by having such similar paradigms across frontend and backend.

[0] - blog series I wrote detailing Angular best practices that I learnt along the way: https://link.medium.com/ncYgWgnK2nb


The whole "I hate angular" is a bit old at this point. The latest versions are really quite simple/powerful. We act like it's still 2015 and we're jumping from angularjs 1.x to React. The jump was nice, yet turns out production-grade engineering in React was not so nice.


I don’t like the separation between template and code, I don’t like the developer experience (slow and often malfunctioning VS Code extensions and slow builds, bad in-browser debug helpers), I don’t like how creating “ad-hoc” components is basically impossible.

I think Zone.js is insane.

I know Angular very well. That’s why I’m confident I can create high-performance applications using Angular. I firmly believe Angular has far too many pitfalls for inexperienced developers.

So yeah, I hate Angular.


If you can't hate on your major dependencies confidently and at length, you probably haven't used them long enough yet.

I generally spend much of my day writing perl. It absolutely works for me, but every time I encounter somebody who wants to -attack- perl I find myself disappointed by how short their list of reasons to hate it is compared to my own.

(one of the most complicated JS codebases I work with on a regular basis is in angular, and the learning curve is a fucking cliff but yeah, once you get to the top, I agree with everything you've said about it)


`Angular Language Service` is the only domain specific extension I've ever needed and its never been an issue. The template separation, as well as (s)css, I like organizationally compared to the chaos you end up in with large jsx-styled applications.


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.


The reason for the virtual DOM: in react, the entire world is a function that gets rerun on every render. The whole point is to be able to define some state and then render the whole world in accordance with that new state. When react was conceived, Dom updates were very slow and could bottleneck the application.

Knowing that your entire UI is running inside a function that gets re-executed with every render, gives a tremendous amount of safety and predictability to your application and makes testing a breeze. But you can't actually rerender the whole world every time because performance, so instead React relies on a JavaScript object to hold a representation of the UI, and then surgically updates only the pieces that need updating. This was React's big thing.

The reason people want to move away from virtual dom is because browsers have gotten faster and now the Dom updates aren't the biggest source of overhead.


https://svelte.dev/blog/virtual-dom-is-pure-overhead

It is a huge performance issue.

I tried to do a pokedex in react, you have to use a virtual list, because react/virtual dom is too slow, doing any operation on a plain list with 1k element, like filtering lead to multiples seconds freeze.

This also lead to a lot of issues, like not being able to ctrl+f text being out of screen in a virtual list.


I don't understand your issues here. I built a community app for a hockey league that manages around 120,000 players in the DB. I do tons of loading, sorting and various algorithms on the data. I've never once had React be my bottleneck.

Rendering a page server-side and delivering it to users is about as inefficient a process as you can get unless you have massive resources dedicated to an optimization almost no one needs anymore. The virtual DOM is extremely efficient, it just needs more memory and CPU cycles on the client which are resources readily available. Browsers are very good at managing this in 2022.

Btw, your code in that article is completely irrelevant... I would not be showing people that article in 2022.


I bet you don't display the 120k players at once on the front, you use some sort of virtualised component (like ag-grid) thats put in the DOM only what is displayed on screen, and keep the whole set in memory.

It cause an issue: now the search in document browser feature will not work properly.

This is acceptable the usecase you are describing(due to the huge dataset), but a lot of interfaces use such virtual list, and it's an PITA for the user.

Take Azure DevOps for example: In a lot of place, you can't ctrl+f a project outside the screen, even if it exist, you have to scroll to it.

> Rendering a page server-side [...]

Nobody talked about server side rendering there.

> The virtual DOM is extremely efficient

Did you read the article I linked ?

The virtual DOM is not "extremely efficient" it's only good at removing uneeded DOM modifications. Not doing these uneeded modifications in the first place is faster.


I have hit a point where I've moved my browser from running locally on my laptop to running on a hetzner dedicated server over VNC because it performs ridiculously better so "readily available" is apparently an unfortunately variable claim.

I would note however that my laptop is not entirely recent and I'm using firefox and my big pain point has been React Native Web apps like web twitter. So you're likely still mostly right, and the question inevitably becomes "how much mostly is enough for any given application".


what are you using for VNC with hetzner? interested in trying this out


https://tigervnc.org

Does some impressive compression tricks that results in it being remarkably snappy.

You may prefer to run Xvnc directly rather than use the wrapper script, depends how you want to set everything up.

I will be honest here that my setup is not properly automated yet because it's inside a jail on a FreeBSD install and the underlying box gets good enough uptime that my current half-assed approach hasn't yet annoyed me into finishing it.

There weren't any interesting gotchas though.


Great article. The fact that Angular, the framework built by the company who also builds the world's most popular browser, does not have a virtual DOM, is very telling.


I wouldn't acclaim angular for the performance either.

Building apps on it show consistent subpar performance.


generally speaking, Angular handles dom manipulation better than React and Vue. Angular has managed to simplify its enterprise/production applicability and performance is a first-order feature that's hard to f up. React -- you can f performance up before you're done writing your first component.


It's actually a terrible article that has no relevance in 2022, and it is even misleading for 2018 standards.


I also did sort-of Pokedex a long time ago [0], but haven't seen any performance issues. No virtualization whatsoever. Code [1] is rather simple.

[0]: https://mlajtos.github.io/lb-pokedex/build/

[1]: https://github.com/mlajtos/lb-pokedex


I ran a profiling tool. I searched "zz" then deleted these.

Deleting it caused a 120ms UI freeze (and I notice it :p):

Profiling report: https://share.firefox.dev/3C3OhIq

Given I had slightly more entries (a hundred more) and that I had way more node per entry, it led me with way worse performance.

Instead of a plain list I have a little summary card per pokemon (which is why I have more node per entry).

The naive implementation in Vue run flawlessly(sadly no preview):

https://github.com/Kuinox/kuinox_pokedex/

Note that the react implementation do weird thing because I tried to get around the issue without success.


You can also preserve DOM and just hide non-matching elements, like here:

https://codesandbox.io/s/hidden-dawn-odm1m1?file=/src/App.ts...


I've spent the past month kind of rewriting parts of Solid in an attempt to better understand it, and coming from React the way Solid works is just so much more beautiful, it feels liberating.

There's no rules of hooks, no dependencies arrays, no stale closures, no wildly different resulting performance depending on where exactly you put your components boundaries, no VDOM at all, no props diffing, when I change the state corresponding to an attribute or property that just gets updated immediately, the way deep DOM nodes structures are created is sooo much more efficient... it's amazing!


Regarding the example under "Reactivity, not lifecycle hooks":

Does the <Counter /> component reference the same outer "count"? So is "count" here global or local to the component? In other words, what is the scope of "count"? Does it change based on where it is placed? If I create multiple <Counter /> components, do they all reference the same "count" or is it different for each component?

Sorry this question might seem naive if you are experienced in SolidJS. I haven't given SolidJS a shot yet (though it is on my list of things to check out).


I think the count variable is quasi an Rx subject, it has identity and any code using it is keeping a hard reference on it. It would probably be GC‘d if nobody referenced it. In my understanding, yes, multiple components would use the same instance of count.


Yeah this is correct. The trick to this is that the subscriptions happen in our JSX and hooks. And really is just a nested tree of `createEffects` if one ever re-evaluates or is disposed it releases its child computations. So while the lifecycle isn't tied to components it is still hierarchically structured. Ie.. places where logic can branch becomes the owning scope, like conditionals or loops. So Signals like count don't really matter where they live and will live as long as in scope or referenced, the rendering still largely defines how long things are around.


I love solidjs, but the similarities to React are the hardest part for me. The semantics are so similar, but the mechanics are the polar opposite.

In react, everything is a function and all your code runs on every render unless you specifically tell it not to. This really encourages a certain style of writing code and provides a lot of guarantees about safety and scope.

Solid is literally the polar opposite, your code runs once, and only the parts that you specifically make reactive are reactive. This allows for much finer-grained updates and performance.

The mental models are so different because they are optimized for different things. React allows for developer sanity (cue all the people who worked on one lousy react app telling me that it doesn't) and Solid optimizes for speed and simplicity.

Solid is very well designed though. One of the features I loved is that Ryan specifically built it to work as possible to vanilla html/js so you can copy old stack overflow answers.


I had the same problem: spending time trying to figure out why something didn’t paint. You also can’t use restructuring the way you might expect. It’s a cool library, though. I think you just need to keep a different set of nuances in mind when using it.


Agreed, and typescript is essential. I'm still new enough to the patterns used in solid that I can't always tell whether a prop is a value or a signal, but with TS it's trivial.


and Solid JS runs very close native JS speed! Any one making a Reactive Native Connector for solidjs? Vue Native was already using that connector. with that, Solid JS will be ultimate isomorphic lib to use.


The author of Solid.js (Ryan Carniato) also does weekly livestreams about a range of JavaScript topics:

https://www.youtube.com/c/RyanCarniato9


As part of my annual routine, I'm exploring the "latest and greatest" JavaScript UI library. Solid.js's performance seemed compelling. 15 minutes into documentation and this is what I encountered. Can you guess which one of the ChildComponent* updates when the text input on the parent component is updated, and the props passed to the child?

  export default function ParentComponent() {
    const [value, setValue] = createSignal("");
      return (
         <div>
            <ChildComponent value={value()} />
            <input type="text" oninput={(e) => setValue(e.currentTarget.value)} /> 
        </div>
    );
  }
  
  const ChildComponent1 = ({ props }) => <div>{props}</div>;
  const ChildComponent2 = (props) => {
    const value = props.value || "default";
    return <div>{value}</div>;
  };
  const ChildComponent3 = (props) => {
    return <div>{props.value || "default"}</div>;
  };
  const ChildComponent4 = (props) => {
    const value = () => props.value || "default";
    return <div>{value()}</div>;
  };
  const ChildComponent5 = (props) => {
    const value = createMemo(() => props.value || "default");
    return <div>{value()}</div>;
  };
  const ChildComponent6 = (props) => {
    props = mergeProps({ value: "default" }, props);
    return <div>{props.value}</div>;
  };
  const ChildComponent7 = (props) => {
    const { value: valueProp } = props;
    const value = createMemo(() => valueProp || "default");
    return <div>{value()}</div>;
  };
  const ChildComponent8 = (props) => {
    const valueProp = props.value;
    const value = createMemo(() => valueProp || "default");
    return <div>{value()}</div>;
  };
The answer is 3, 4, 5, 6. My takeaway is this: there are multiple ways of doing it right, but also a handful of gotchas. The only way to be safe is to keep the mental model of these partitions while you develop, test, debug, and code review. As a code reviewer, you can easily accept the wrong code. For now, I remain skeptical of Solid.js as solving the complexity woes of Reactive programming. I'm not sure React.js is better.


Or Vue either to be fair. Similar rules in the Vue's setup function. It's how reactivity works in JavaScript. Basically don't destructure or access values out of of primitives or JSX. That's basically the gotcha.

Unfortunately it's the price you pay for portability thus far. You can build your own language around this like Svelte but then composability is limited (need to rely on other mechanisms). You can make the updates coarser grained like React but then you need a different mechanism(like VDOM diffing) to apply updates granularly. I imagine this situation improves in the future but we haven't gotten there yet.


Out of interest, what language features do you see JavaScript needing to support these kinds of fine-grained reactive updates, and to allow destructuring?

I'd assume something like es6 proxies for primitive values would be needed, but that, nor built-in reactive primitives, are being discussed currently as far as I know.

Personally, I'd love to see reactive `[].map` equivalents (no need to use <For> components), but that's possible today with a reactive wrapper or (god forbid) patching the prototype.


How is the setInterval cleared when Counter is no longer rendered? It seems like a leak.


It's certainly a leak. That setInterval is going to be holding the closure context alive which will reference the component in many frameworks.

The setInterval will also keep firing causing, wasting CPU and battery.

It's hard to take any examples here seriously without showing proper cleanup that would pass code review.


Good to see other people spotted that, I noticed it immediately and had a "there's a bug! a bug!" alarm bell ringing in my head throughout the rest of the article. I may have some sort of setInterval related PTSD.


The issue is more about not understanding how React works and how to use hooks. The setInterval example is easily solved. No, you can't just half-assedly guess your way to a correct solution.

Also, composing asynchronous behavior with useEffect is also not a good idea. Don't abuse useEffect as an Event Handler (too much). Think about using Redux (+ thunks/saga) or something similar.


This looks a lot like my current favorite state framework (which yes is done by Facebook people), Recoil - https://recoiljs.org/

Your naming however reminds me of the RxJS and general reactive programming paradigm I've always pined after... some combination of the two would be my UI state management holy grail.


Many devs have succumbed to the siren song of event sourcing. And many code bases have sank on those rocky shores.


I'm surprised to see so many negative opinions about React. Surely, I can't be the only one that truly likes React and finds myself being extremely productive and making impressive apps with it.


You're definitely not the only one. But I find it refreshing to hear some people voicing many of the complaints I've had about React for years. Up until very recently, criticizing react seemed to be nearly unheard of.


Have you tried solid/vue? If so, consider adding your perspective of what react does better.


I haven't used Vue in a really long time. Last time I used it, Evan You had just shipped Vue 2.0. Using Vue was honestly a fantastic experience, and a great stepping stone to get off of Angular 1.x. I really enjoyed its approach to data binding and single file components where the styling, functional logic and templating was all contained in a single file.

At the time the promise of React was very far-fetched: using JavaScript generate your HTML markup. I actually was really adamant to use React and held off on it for a long time. However, once I gave it a fair shot, I was blown away by how intuitive it felt to use. I think the mental models it champions really helped drive its adoption.

While it was a pleasure to use, I was always wondering about other frameworks. But where React really locked me in was in its support. `create-react-app` was an incredible achievement, tbh. It made it so easy to start using React and have all of the bells and whistles out of the box without ejecting. And then, once you do eject, all you have to do is modify your Webpack configuration and you can get all of the additional stuff you want. The community also made incredible packages, like Downshift and Emotion, which further made React an attractive tool.

Over the years the React team has kept innovating it. Hooks made it so much easier to just write functional components, which has always been a core tenet of React (admittedly, Solid.js was using hooks before React). More recently, React added the concept of SSR/hydration to its framework. Initially a lot of people made fun of it because it was like we went back to HTML generation on the server, but then developers realized that you get the best of both worlds: immediate markup from the server with the reactivity and snappiness of a single-page application.

Because of SSR we now have innovative frameworks like Next.js and Remix. I know Vue 3/Nuxt.js exist now, and I know Solid.js exists now, but now that I've started using Remix I'm trapped in React land again (and honestly, couldn't care less). Remix is so freaking good and it's such a freaking improvement from when I did SSR in 2019 that it's hard to see myself using another framework.

This is kind of a bad answer, but at least I can show you why I'm locked into React I haven't tried any other framework (I also genuinely enjoy using React).

There are some things I don't enjoy about React. I don't enjoy how prevalent Redux is when developers think about global state. I also don't think contexts are a good enough solution (having a good reactive model would have definitely helped here). There are other things I don't like about React: I don't like how often it calls functions; it's extremely wasteful in large applications if you're not careful. And the semantics of `useEffect` are really murky for newcomers (and even sometimes for experienced developers).

If there is something like a Remix equivalent for Solid.js I'll give Solid.js a shot. But right now I'm in love with Remix. Maybe I can use Remix with Solid.js? I'll take a look.


I wonder if there's an immediate mode framework for Js, in the vein of IMGUI for C. If people are wondering what that is, it's basically this:

  function renderGui()
  {
     label('hello')
     if(button('click me'))
     {
          console.log('I have been clicked')
     }
  }


This seems like a huge step back from declarative UIs, any reason why you would say it’s preferable?


Performance, for one. This method can easily avoid allocating an object for every div (since it turns into a method call, not something that needs to return an object), compared to React's render() method.

Another scenario outlined in the article of rendering a row of buttons, each with it's own click handler, that needs to allocate a lambda for each button every render. Alternatively, using map() and filter() also allocates lambdas and temporary arrays.

All this can be replaced with a simple for loop like:

  for (item of items) {
     if(!item.isClickable)
         continue;
     if(button(item.name)){
         console.log(item.name)
     }
  }
Compare this to the React-ish pseudocode:

  items.filter(item=>item.isClickable).map(item=><button onClick={()=>console.log(item.name)} >{item.name}</button>)

Readibility-wise, it's sort of an acquired taste, I'm not saying it looks better than JSX (but it's a hell of a lot readable than React.createElement, so some syntactic sugar might be put on top of it).


I suspect that the nature of the DOM means that being able to do that in the first place would actually require a virtual DOM w/diffing assuming you're targeting browsers.

However that's probably obvious, so: Assuming you're considering greenfield / blue sky thinking here, it's worth noting that v8 has had so much money and engineering hours sunk into it that the react-ish pseudocode probably doesn't make nearly as many allocations as a naive reading of the code might expect.

(a good friend of mine was very surprised to discover that v8's JIT is good enough that naive javascript code was basically competitive with FFI-ing out to C++ just because the node FFI layer introduced sufficient overhead that the JIT managed to catch up ... I am not a compiler wonk so please don't take this as a remotely expert pronouncement but "it is, in fact, quite difficult to overestimate v8 these days" seems to be a solid heuristic)


>the react-ish pseudocode probably doesn't make nearly as many allocations as a naive reading of the code might expect.

Who knows, if V8 manages it to turn it into a for-loop? I don't, but after a quick googling, as of 2018, it certainly didn't: https://github.com/dg92/Performance-Analysis-JS

The problem with JS is that the execution model is so nebulous, that performance advice basically boils down to - trust in Google.


Everyone goes on about performance but unless you are doing something extremely complicated or you have fucked stuff up in an extremely bad way, the actual part of React apps which is rendering to vdom and diffing to dom, in my quite long experience with it, is very rarely a problem, and not enough to warrant a stateful imperative approach.


i think an immediate mode UI library would make sense if you want to render to a canvas or a similar "backend". maybe for things like games, or some interactive graphic things

but if you want to render to the DOM —and you want to use the DOM if you want to have decent accessibility, or not have to reimplement all the complex user interactions that the browsers take care of— then i think an immediate mode framework might have a great impedance mismatch with the DOM, as the latter is a retained mode GUI model


This article perfectly captures the essence of why I no longer work in web development. Never again!


Out of curiosity, what do you do now?


Google search says senior eng leadership.


One of the most compelling things about Solid.js is that it integrates some of React's most important (IMO, probably aside from the central idea of UI as a function of state) ideas for writing applications, Suspense, ErrorBoundary, useTransition.


As someone who doesn’t feel so enamored with React’s hooks, I find this compelling.


React hooks are footguns


Vue hooks avoid the closure/scoping issue mentioned in the doc. Very similar conceptually, but I find Vue's implementation a bit nicer to use


Took me 30 minutes to get up and running in Vue. Week 4 of this job and I still can't unfuck myself with React.


I'm missing some toes from the old react lifecycle methods. The vanilla hooks were easy to use, reason about, and robust against misuse in a way the lifecycle methods weren't.

I could see custom hooks being more dangerous and difficult, but Higher-Order Components are too.


I always wonder why all JavaScript client-side frameworks have such distinct design.

No other dev environment for building GUIs has react-like components.

What’s so special about the web that we keep creating frustrating frontend frameworks for?


I personally love the front end frameworks, and I don’t find them frustrating at all. Knockout was a workhorse until angular came along. And then vuejs. And now I use sveltekit for pretty much any web app.

The frameworks are popular because you can build things incredibly quickly once you know the ins and outs of your framework of choice. The component-style design, the endpoint design, so much is just driven by the needs of web-based development and the framework creator’s preferred way of abstracting away some of the challenges.


Flutter, Jetpack Compose, and SwiftUI all have React-like components.


The browser was intended to render documents, not applications.

In particular, HTML has a tree structure, which means that things that are semantically related on the page and update together are often miles away from each other in the tree.

And the page in the browser is part HTML, part CSS, part Javascript. Frameworks try to let the developer work in JS only and generate the rest.

And finally, much of the application's state is often kept in a backend, and access to it a asynchronous.

I think that's why Web development is so distinct from other UIs.


One reason is probably that creating UIs programmatically was historically very cumbersome in JavaScript due to various issues that are no longer relevant. That means everything had to be HTML-based hybrids of some sort.


Because there are a lot many js devs than others. Hence js ecosystem is significantly larger.


This is the same code in Svelte.js

# Counter.svelte

<script> let count= 0

  setInterval(() => {

    count += 1

  }, 1000)
</script>

<div>The count is: {count}</div>


What does a reusable (ie import from another file) `useAutoCounter` look like in Svelte?


Probably the equivalent would be a custom store, something like this:

  <script>
    import { writable } from "svelte/store";
  
    function autoCounter(interval, initialValue = 0) {
      let { subscribe, update } = writable(initialValue);
      setInterval(() => update((n) => n + 1), interval);
      return { subscribe }
    }
  
    let counter = autoCounter(1000);
  </script>
  
  <div>The count is: {$counter}</div>


Yep. Looks sort of like the Solid example in the article. It's basically my auto-response to Svelte syntax. Once you do anything in Svelte it is more or less the same thing.

Just different priorities. In Solid you can take that code as is in the component and hoist it out to a function above and presto.. store. It's all the same thing everywhere. Same patterns, same usage, same building blocks. No new APIs, no new syntax.

It is nice when first learning not to worry about Svelte Stores and use the convenient syntax. It is also nice to learn something once and use it everywhere.


After years of using Knockout and Angular/RxJS, I'm not at all convinced that adding Observables to React is going to lead anywhere interesting.


This feels a lot like knockout.js but with a jsx syntax.


Knockout.js was/is wonderful, I'm not sure why it never took off the way angular or react did. I do appreciate jsx though so I'll be looking into Solid.


As far as I could tell, around that time (and even now to some extent) the amount of activity on StackOverflow is used to measure the popularity of a project.

Angular had a "made by google" kind of logo on its website, indicating to many people that it's of high quality and worth adopting.

But Angular was also so convoluted and had so many problems that so many people kept running into random problems all the time and had to ask questions about them on SO. This signals (incorrectly) that Angular is popular, driving more people to believe it's worthwhile to adopt it.

Knockout had neither. It was not sponsored by a corporation. And it was so good that you hardly ever run into random problems.

Ultimately it was eclipsed by React and Typescript because lack of type checking for the html templates means it's hard to scale it will to large projects.


The problems with knockout were:

very hacky syntax embedded into html. Sometimes you had to even use some kind of comment notation because there was no entry point into the html to add data properties or whatever it had.

it was slow.

the observables weren't variables you could use like plain JavaScript variables

it has the same problem as React - state-based algorithms are not very good ways to solve problems (if (showDialog && !open && ranOnce). You have to keep creating more variables to represent more states instead of using normal programming language concepts, and then all of the observables ping around and become complicated.


It kinda is, but it's more than just JSX. Solid was basically born out of Knockout and is in many ways a continuation of Knockout, but it has some relatively significant changes and additions to make this approach more viable, especially for large long standing projects. JSX is more of a quality of life thing, you don't even have to use it, Solid's runtime exists completely separately of JSX and can work great without it or even potentially be integrated with other templating languages.


This is really interesting. I'm happy the author covered the effects side of things because this is where I've had some challenges with non-trivial React apps. Sometimes effects can be very complicated and result in React getting stuck in an infinite loop.

Before you say it's because I'm "doing it wrong", Material UI's website had this problem for quite a long time. That issue required doing manual refreshes to not trigger the infinite loops (something you would only see in the debugger). This problem in reactivity is a issue in React that even the best devs can struggle with. My solution was simple... use useEffect less. Depend less on reactivity.

The bottom line for me is that I feel like I can accomplish anything in React, but the devil is in the details. There are so many scenarios that I would need to vet with Solid.js, but if it works better at managing a complex app's reactivity, it could be very compelling.


I am fundamentally skeptical of anything that needs a runtime for anything other than security and profiling.

I am extra skeptical of any framework that needs 50mb memory baseline.

I am triple skeptical of anything that uses brand-new packages and components like legos.

*Iterations on React are welcome.*


Well, you can be skeptical, and the enterprise front-end engineers are going to go right on using that stuff because it scales to large codebase.


Yes, right up until said startups and enterprises 1. cannot afford the outrageous cloud expense or 2. Need to scale to low-power devices (such as wearables) and to customers who are security conscious


Don't worry, they're doing fine. Anyway, we were talking about React, to which none of those objections apply.



As a svelte developer i could really not understand the problem of TypeOfNaN.


What feels like complete insanity when it comes to React is that it needs compilation to work:

    render() {
        return <div>The count is: {this.state.count}</div>;
    }
This is not Javascript and it will need to be compiled into Javascript before it gets executed.

But you can do the same in Javascript:

    render() {
        return `<div>The count is: ${this.state.count}</div>`;
    }
Using regular Javascript makes my life a thousand times easier than having to go through a compiler to make things work in the browser.


This is another area where Solid has an advantage over React. With React you could use Hyperscript functions so the above JSX would be

React.createElement('div', null, `The count is: {this.state.count}`).

With Solid you get the choice, you could use JSX, Hyperscript functions or template literals like in your example. Solid does recommend using JSX because there are some tradeoffs to using template literals without compilation, but it still maintains almost all of its qualities AFAIK and in the big framework benchmark Solid with template literals is the fastest template literals implementation.

With Solid you don't even have to use any templating languages, you can take care of rendering yourself using the tools that the framework gives you. Solid at its core is more of a capable state management library similar to MobX but designed to be used as the only reactivity engine unlike MobX which is usually used on top of React.


Using template literals to represent html is a security issue. If the state comes from the user, they can add script tags into the html. People try to solve this with tagged templates, but then if you forget the tag, you have a security issue again. Lit checks for this, but the fact that it has to check means it is less secure than not using tagged templates. There are libraries on github for creating sql using tagged templates which have the same security issue. The problem is that if your function works with both tagged templates and plain strings, when you forget to add the tag, you will never know.


    If the state comes from the user, they
    can add script tags into the html
How is that different with React?

And how is it a problem? A rendering engine would set the html of some element to the html I think?

This ...

    document.body.innerHTML='<script>alert(1)</script>';
...does not execute the script.



In terms of the placeholders in JSX, no they are escaped.


True.

But the same issue with reacts way.


Doesn't that make an hard life for the linter that now has to decide which template string represents a (complete) element and which is just text/whatever? I prefer the first solution because it's much simpler to parse


You might find this interesting: https://github.com/developit/htm#readme


<cynic>Yeah React is already so last year.</cynic>


If theres one React recommendation I'd make is: use Mobx.


Did anybody use Observable Hooks yet? https://observable-hooks.js.org/

This seems to be quite a good way to compute and apply complex state to a component. In my opinion better than useReducer().

But to be honest, for most components useState() and useEffect() are totally enough. And if you do something wrong like in the counter example, you see it right away.


> That’s a lot of code to write for an auto-incrementing counter.

Is it though?

I mean I happily write more code for a single-page Vue component for something like this.

Terseness is not always a virtue.


> Terseness is not always a virtue.

This is a subjective thing, right? Personally, I hate boilerplate: either it's a distraction because it's boring and superfluous, or worse it's long and it's wrong. Regardless, it adds to the cognitive load when maintaining code.


Yeah, definitely, hence the "not always" in this case.

There are several terse syntaxes I really like. I'm just not convinced it's a virtue in this context.


The vue code for this is pretty terse anyway to be honest. A single variable in data, a call to setInterval in the created hook and a few lines of html with template formatting. It's extremely clear what's going on too.


I agree. I think the single-page .vue model is also really amenable to whitespace and consistent formatting.


Isn't this just the Vue composition API dressed up to look like React? I haven't done frontend in a minute, so apologies if this is ignorant.


Mostly except there is no VDOM, the reactivity applies right down to the DOM binding. And it predates Hooks and Vue's Composition API.


I tried several React alternatives.

At this stage, size of community is a really important factor, overriding many other factors.

It's just incredibly important for there to be tools support, questions answered on stack overflow and a community of people developing related software.

At this stage there's only really VueJS, Angular and maybe one or two others with community large enough to justify changing.


Just started back in October, but you can talk to me directly and get questions answered pretty quick: https://github.com/cheatcode/joystick


Instead of vDOM, solid.js uses <template> - real DOM elements + cloneNode().

And that's doubtful decision. Each live DOM element is about 100-200 bytes in memory.

While in React (PReact, Mithril) this JSX expression:

   <div>a</div> 
is this:

   ["div",{},["a"]]
5 or so times less.

In other words: needs to be tested on large DOM structures.


That's just a component template though. When you produce large DOM trees, you usually do so by rendering your components a lot of times, not by having a lot of different components


I just dove back into react after a while in vue and deep in some backend systems.

I love the new ergonomics (hooks, useEffect, redux tools) but I definitely hit the exact issue OP mentions almost immediately.

I don’t feel that the solution react provides is too out there, but agree it could be better. The react ecosystem still makes it worth the one or two “could be betters” for me.


You may need to think about how you implement asynchronous side effects. Declaring them with useEffect quickly gets annoying. Look for things like thunks or saga. Or when not using Redux, at least think about using async functions.

In general I want to keep async logic out of my components as much as possible. I also don't want the component rendering to drive the async logic.


have you had any success using the useReducer hook totally outside of redux? I feel like its bulky for certain operations but I still haven't found that "perfect" use-case for it yet that makes it click for me


So far not. But I can imagine things. For example imagine a Game of life component with two actions, "evolve" and "set_cell". Basically any time when it is awkward to have exactly one mutation function (setter) for a piece of state. Other examples are a minesweeper game. Basically any time it is too awkward to compose different mutations on top of a single setter.

The other big benefit is that you can test the reducer without the component. And compared to redux, it still has a smaller footprint. So it doesn't have the dependencies, you don't need to setup a store etc.


So, its React with the sharp edges smoothed away. Interesting. I am not a web developer, so forgive me if this is a stupid question, but can someone tell me, if you decided to use Solid.js as opposed to React, can you still make use of all the 3rd party React UI frameworks out there? Is it compatible with React in that sense?


Unfortunately, you cannot use React code inside Solidjs[1] so you cannot make use of the huge ecosystem of react UI components/libraries. It isn't compatible in that sense. However, there is an "official" option[2] for including Solidjs code inside React.

  [1]: https://www.solidjs.com/guides/faq#is-there-react-compat%2C-or-some-way-to-use-my-react-libraries-in-solid%3F
  [2]: https://github.com/solidjs/react-solid-state


Ahh right. I see. Thanks for the info and the link.


This article points out how ridiculous React is. This is just a simple timer, yet there are so many things you can get wrong. This article doesn't seem to even clear the interval so it runs forever. Solid solves some problems, but I don't know why everything has to be reactive. I have never understood it, I just noticed that from knockoutjs onwards, every front-end framework was based on automatically updating views based on state.

I create a library at github.com/thebinarysearchtree/artwork. To be honest, I don't know if it is going to work or not. It is just javascript with no reactivity. When I rewrote a fairly typical large application that you would find at most corporations (not a giant tech company application), every component ended up being about 50% of the code of react, with excellent performance.

The problem I have with it that makes me uneasy is that ultimately you are creating content in JavaScript, which I don't think is ideal. The lack of HTML structure isn't a problem as devtools and so on show that. The problem is things like:

  const label = label('Email:');
I want the HTMLLabelElement variable to be called label... but that is the name of the function that creates it. Then there is the setText example on the github page... I just didn't want to write label.innerText.. h3.innerText... etc etc .innerText. Variadic arguments are not ideal though, and when you have lots of elements with lots of attributes, eg:

  const content = div({
    className: 'content',
    title: 'whatever',
    innerText: 'something'
  });
    
it doesn't look like art, which is the point of the library. It is just hard to create content with JavaScript and not really the tool for the job. If I could solve that, I would be very happy with the library. It kind of needs JSX, but then it is back to having the JSX variable auto-update the state and well.. I guess that is why knockout and so on do it. Maybe I could do just plain HTML without state.

So yeah, I don't know. I mean, it does end up being half the code. If you look at the todo example on my github page, and compare it to the one on React's homepage or solid's homepage, it is literally half the amount of code with native hand-written performance. This continues on to real-world components, it is just that.. I am a perfectionist I guess, and I want it to be elegant always.

anyway.. I haven't even finished writing the documentation.


I worked in React for a few years, although not enough to ever feel like I was an expert. This article resonated with me because I also got off the bus because of React hooks.

I've been using Lit.js for about a year now and something about it just clicks. I just wish it were more mainstream.


I prefer the very explicit first React example. While I'm not a web dev, on a larger project with a larger team, implicit stuff generally makes things harder to understand and more error-prone. I don't see the boilerplate as a negative, I see it as documentation.


It's cool, I like this methodology a lot because it can piece together functionally really well.


I'm used to react and hooks,and right now I can't do any better. Doesn't mean I like it. The solid example here seems to have a similar complexity and implicit knowledge as hooks just a slightly different syntax


There's light at the end of the tunnel. Don't fret, traveller: https://github.com/cheatcode/joystick.


javascript ecosystem is truly a shithole. I say this independently of the article.


Escape the nightmare, embrace the zen, use Joystick: https://github.com/cheatcode/joystick.


Sips tea and happily keeps writing class components with Redux


Wait what's the problem here? Because you didn't like adding [count] as a dependency to setEffect(()=>{},[...count goes here...]) you wrote an entirely new framework?


It's not just that one case. Everything in react is like this. I find react code hard to reason about because this type of friction exists almost everywhere. Solid is what I always wished react was, but had a hard time articulating.


I don't know.

I used Vue.js for my last development, and I see no need to trade Vue.js for something that's both more complex and has less performance.


What was wrong with the class component? It was very understandable and predictable.


Class components are fine for the simplest examples, but the moment they start increasing in complexity, they become a bit of a mess. Sharing functionality across multiple components becomes tricky with class components (with the only real option being HoCs / render props). You necessarily have to spread logic across different lifecycle methods.

Hooks allow you to bundle code together by functionality, and consequently allow you to easily extract and share said functionality in a very composable way.

This is my go to for visualizing the difference: https://i.imgur.com/e9K8vfz.gif


> Hooks allow you to bundle code together by functionality, and consequently allow you to easily extract and share said functionality in a very composable way.

You’re using React. The mechanism that enables code reusability is through composing components.

There’s nothing wrong with class components even for the most complex logic. The only downside is the community has moved on and mostly adopted hooks and functional components.


> The mechanism that enables code reusability is through composing components

If you think that this:

   return (
     <Apple>
       {({ apple }) => (
         <Slicer slice={apple}>
           {({ sliced: slicedApple } => (
             <DinnerPlane contents={slicedApple} />
            )
           }
         </Slicer>
       )}
     </Apple>
   );

is more desirable than this:

  const apple = useApple();
  const slicedApple = useSlicer(apple);
  return <DinnerPlate contents={slicedApple} />;
Then go for it I guess.

Although you'd have to somehow ignore the fact that you'd end up with an even bigger mess if you want these intermediate functionality steps to interact with the parent component in a non-trivial way, ..

Needless to say, I have written such render-prop and "renderless" components in the past, and I see very little upside compared to hooks.


Thats interesting. So in your 2nd example, am I right in saying the variables 'apple' and 'slicedApple' are react JSX that you are ultimatly passing through to <DinnerPlate/> to render on the screen? If so, yes, that is fairly intuitive.


'apple' and 'slicedApple' would more likely be strings / objects / arrays in the real world. Maybe this still woefully contrived version is clearer?

  function FacebookComment({ commentId }) {
    const comment = useGetComment(commentId);
    const likes = useGetLikes(comment);
  
    return (
      <div>
        <span>{comment.text}</span>
        <span>{likes.length} likes</span>
      </div>
    );
  }
I don't think deal with more braces right now to bring the alternative into existence unfortunately


Ohh i see. Yes, that makes sense. Thanks for taking the time to reply.


???

  <DinnerPlate>{...map <Slicer><Apple /><Slicer/> }</DinnerPlate>


Thanks for the viz, haven't done React in a while so it's interesting to see how things have improved. The new paradigm seems alot better modularized and the logical structure seems easier to follow, combined with it taking advantage of newer JS features.


I think in the class world this is normally done by writing a service and injecting it on the constructor of the parent class. Since, React doesn't have DI built-in, it becomes problematic with the injection. hooks are essentially classes imo.


How does it become problematic? You just pass in dependencies as arguments to functions or components.


Unnecessary coupling...something...something...


Because classes as a language construct are one big massive footgun on nearly every level.


Every time I see stuff like this

    componentDidMount() {
I get driven away from React. It looks haphazard. Seriously? What about

    componentDidntMount() {

    componentWantedToMountButDidnt() {
...

I'm used to clean naming conventions like

    void Component::on_mount() {
        ....
    }


When does on_mount run?

React's componentDidMount() is a lifecycle method that runs after a component mounts, once it's been inserted into the DOM. This is in contrast to componentWillMount() (now UNSAFE_componentWillMount(), because it breaks when async rendering is enabled), which is called before the component mounts.

This naming scheme becomes even more important for the update lifecycle methods-- in addition to componentDidUpdate(), there's a shouldComponentUpdate() called before it (where you can return true/false to tell React whether or not to proceed with the update) and UNSAFE_componentWillUpdate(), called between those two.


Maybe the name is trying to communicate it happens after the component mounted instead of just before


`on_mount_complete` sounds more professional


Agreed; the existing name is not great.


Well, modern react is pushing folks to use the hooks model which jettisons the whole componentDidMount and other class functions. Now you use a side effect hook: https://reactjs.org/docs/hooks-effect.html This is a little more clear that it's for side effects of the component being put in the DOM, but it does require a bit more knowledge of react and hooks.


The reason behind this different naming convention comes from the diversity in experiences of the original team. This will/did prefix instead of “on” comes from Mac OS X/iOS patterns and allows the name to convey the “when” of the listener. It is not “during component mounting” it is “after it did”.


I guess this is just a limitation of JS the language showing itself. Other languages have actual aspect-oriented support where before/after/around methods are very clear name-wise and semantics-wise (if not always so clear without tooling support what happens if you come across a 'mount()' call). Maybe it's time for a new JS framework! /s


Which languages in common industry use in 2022 have language-integrated aspect-oriented programming support?


Fair point. I hinted with the idea of a new framework that you may be able to bolt something acceptable on top of JS (I'm sure it's been done already in the past with some framework) especially if you just want the before/after/around advice features of AOP without other stuff. Or perhaps go into custom syntax transpiled to JS/TS-then-JS since no one seems to mind heavy build processes these days. For the most common industry use, I'd have to say it's still Java's Spring AOP/AspectJ. Maybe it's not language-integrated, but it's pretty close.

For an uncommon use example of what could be possible:

    (defmethod react.component:mount :after ((self counter)) ...) ; instead of componentDidMount
    (defmethod react.component:update :around ((self counter)) ...) ; instead of shouldComponentUpdate
    (defmethod react.component:unmount :before ((self counter)) ...) ; instead of componentWillUnmount
    ; (the react.component namespace qualifier could be whatever else and not necessarily typed out)
However closely you integrate it with the language, having that machinery generally available seems better for naming and for providing new lifecycle functionality, without everyone having to reinvent the wheel and provide it in different incompatible ways. But it's clearly not a big issue.


componentIsNotThatBotheredButStarSighStarIfIMust() { ... }


I hate the whole javascript ecosystem to the core.

Hey I just finished my progressive web app with these 10 cool react components I found on NPM which fit surprisingly well into our startup’s cloud-native Vue interface.

I tried to put the code up on GitHub but it wouldn’t let me upload a 10gb repository (and that was without our proprietary fork of mysql :)


Too much snark.

We are running an enterprise-scale Angular 13 "SPA" that blows the prior platform out of the water.

It just depends on the team and the goal.


Me too. TypeScript is a disaster.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: