Rich Harriss, the author of Svelte talked about all the features that come to React to speed up virtual dom comparisions, but to me he's framework proved it that virtual dom is not necessary, and not the best solution for a scalable UI.
At the same time there's a huge amount of React code at this point. It would be an interesting experiment to try to compile React code to really reactive code that doesn't need virtual DOM.
I just don't know, svelte feels way too similar to the "magic" of knockout and angular 1.0 when looking at the output... Something I got burnt by with weird edge cases of update loops or desync.
Svelte might have that all reasoned out though, in which case the harder separation of logic and view is the only stickler for me, and transpiling JSX into svelte would be interesting to see.
Svelte changes the semantics of your code. It's great to show off to-do apps and simple examples. But I'd be very wary of buying into a framework that relied on compiler modifications to the semantics of code to make it work. I'm also not a fan of pub-sub style change notifications. It's hard to reason about what a change in state is going to cause since you don't know what's subscribed.
With hooks at least I am able to have a mental model around what happens when I call a change function since it flows top down (instead of bottom up).
Ehh it really doesn't change the semantics of your code, hooks are just iterables and you access them by calling next(). In an alternative world they would be a mapping that you give unique names like useState("mystate", ...) and then order and if statements would be irrelevant.
Also you're just just passing a callback function to React and then giving it some pointers to data used as part of the conditional on when it should run.
Like it's janky and reaches through layers but it's not up and rewriting your code.
> hooks are just iterables and you access them by calling next()
In Javascript, "just iterables" can be put in conditional statements, and they don't cause the outer function to be called again.
> Also you're just just
Ah yes. That "just" again
Edit:
> just passing a callback function to React and then giving it some pointers to data
Thing is, in plain JavaScript if it was a callback function, it could be called anything and live anywhere. However, these functions have to be named with a `use` prefix and have limitations on how they can be called.
And you can put hooks in if statements too, but like iterables it won't have the effect you probably want.
it = ["hello", "world", "foo", "bar"]
shouldbehello = next(it)
shouldbeworld = next(it)
if sometimes_true_sometimes_false:
shouldbefoo = next(it)
# if the conditional is false this will be foo, oops.
shouldbebar = next(it)
Hooks aren't callback functions, they're functions that take callback functions. apparently this is breaking python. The naming convention is just so the linter can identify hooks, they're not magic.
def react_to_stuff(func, var):
orig = var.copy()
def monitor():
while True:
sleep 2
if var != orig:
orig = var.copy()
func(var)
t = Thread(target=monitor)
t.start()
stuff = "Hello"
react_to_stuff(lambda x: print(x), stuff)
Nothing is stopping you from putting useState in an if statement. You just have to be careful since it's an "iterator." For example if you put it in an if branch you will want to also use it in the else branch so "next" is called the same number of times.
Like I really don’t know how to explain this any clearer.
* React stores the state for your hooks in a list which is iterable.
* useBlah is just a normal function that internally calls next() on that list to get the next hook state which is why order matters.
* React checks that the iteratior on the list is at the end to see if you’ve consumed all the hooks and throws an error if not because it’s indicative of a bug in your code.
Like at this point the only thing I can recommend is try writing a toy implementation of hooks and see that they’re not magic, they’re just normal JS, and that the restrictions flow naturally from the implementation.
Like why do you want so bad for hooks to be weird?
I guess we have very different definitions of "semantics" then. Rspec is wild but it's still plain Ruby. I'll give you that it's not idiomatic and it comes with some unusual restrictions on its use but libraries have been doing forever, I guess I'm just used to "you have to call this function to init the lib", "this function has to come after this", "you must call the cleanup macro here", "importing this lib monkey-patches these classes."
You can implement a hooks like interface in an afternoon. I don’t think I could do the same for Svelte’s compiler-oriented approach. Using a compiler means that Svelte is essentially a dialect of EcmaScript, which appears to be mutually intelligible but isn’t always. React used a compiler for JSX which also introduced a dialect, but JSX is optional and very straightforward sugar for a simple function call. The Svelte dialect is not so simple.
> Using a compiler means that Svelte is essentially a dialect of EcmaScript
So are hooks. The fact that you can't put regular function calls (which hooks look like) in an if statement, or have to provide custom "dependency lists" to some of them, or that data returned from them suddenly re-renders (aka calls again) the function they are in tells you that this is no longer regular Javascript.
Exactly, I don't get the React is just Javascript. It's not. You're tied to React just as much as Svelte or anything else. The Facebook propaganda machine is clever I will say.
There’s a wide difference between learning the semantics of an API and learning the semantics of a custom language with a compiler. There are alternate implementations of the React API, like Preact. The semantics of the React API might be complex, but you could implement them in essentially any programming language with function pointers and variable length arrays. You can’t say the same thing for Svelte; which we know is impossible to implement in pure JavaScript, which is why it needs a compiler. This is all anyone in this thread is arguing - it’s an argument about semantics but for some kinds of environments semantics are very important.
I’ve helped teams pilot out of own-the-world frameworks like Backbone and Rails before (both of which substantially distort the runtime they’re embedded in); but it was much harder to deal with Backbone + Coffeescript together. For Backbone w/ vanilla JS or Rails, we could use standard static analysis tools, Coffeescript added a big extra wrinkle to our migration.
Yes and no. They definitely push the boundaries, and the framework imposes certain extra restrictions on how it wants you to use them, and they warp your normal intuitions a bit. But at the end of the day they are just function calls. They don't compile down to some other sort of construct with totally different language semantics.
There's a fundamental difference between a framework that takes your code and does weird things with it, vs a framework that rewrites the semantics of your code out from under you.
Yes, they push the boundaries and essentially create a DSL with its own rules that do change semantics of the language. Not as drastically as a custom compiler, but they still do.
A fair point, and I haven't dove into react's inner workings to see how the sausage (I was more discussing the compiled code of the app), but one can reason "how" hooks work after observing them.
- They're tied to the lifecycle of the component, so they must reach "up" into whatever just called our component function.
- They do not like being moved about/conditionally executed, so hook's likely use an array for metadata storage and position rather than needing explicit "hook keys".
Honestly they are weird, but I can reason about them with a "good enough" mental model to get paid.
I've no clue what svelte does behind the scenes, much the same way as I can't reckon what WASM or assembly do.
So I guess I just have to blindly trust the compiler?
Agree with you completely - hooks really aren't magical at all. The only thing that's "magical" is that React takes hook data and embeds it somewhere in the component instance behind the scene.. something that also necessarily must happen with class components. The state has to live somewhere.
I'm always confused by this comment about hooks. You can sus out the basics of how hooks work under the hood by just looking at their relatively simple rules that govern their usage. More from Dan Abramov: https://medium.com/@dan_abramov/making-sense-of-react-hooks-...
I think they are actually _more_ explicit than classes about how the state is stored. IMO `this` in class based components isn't as straightforward (e.g. https://overreacted.io/why-do-we-write-super-props/)
Yeah I threw together a simple wiki on top of a CRDT using solidjs a couple weeks ago. It’s delightful - it’s basically react but compiled to be smaller and faster. (It looks and feels like react but there’s no giant runtime, and no vdom diffing). And it has a really nice API for reactive state.
I haven’t built anything big with it yet but I like what I see so far!
What Rich Harris said sounds very nice and interesting, but I'd take issue with it being "not the best solution for a scalable UI." As long as the slowdown caused by the Virtual DOM remains below limits of human perception, it doesn't really matter whether something can do without Virtual DOM or not.
I’ve worked on React applications where a single key press would cause lags over 300ms. The React profiler wasn’t able to show any latency in my render functions and using the browser profiler I saw that all time was spent somewhere inside React’s internal.
There’s some real overhead of React’s virtual DOM.
I've worked on dozens of large React codebases, and never once came across a performance problem that was caused by VDOM overhead. Performance lag was always caused by bad application design. In most cases, the performance lag was due to over-rendering caused by a badly designed component architecture and state management system.
That's bizarre; I've spent a lot of time spot-optimizing React apps and never had something like that happen
The VDOM definitely carried overhead in my case, but it was easy to profile and optimize (with the React tools). In many cases, reflowing the sheer size of the DOM we were generating was a bigger bottleneck than the time it took React to render it in the first place
I've seen it a lot, personally. I've even seen instances where the input would delay by over a second. Really wild stuff.
Can a better design help to mitigate those issues? Sure. But I don't like having to wonder whether it was my own design, or if it's something internal to the library.
I think we're talking about two different things- I've definitely seen that much delay before, but it always came down to inefficient rendering logic and/or components that re-rendered excessively often, both of which are easy to detect in profiling and usually not too hard to solve
This is the part I thought I was bizarre:
> The React profiler wasn’t able to show any latency in my render functions and using the browser profiler I saw that all time was spent somewhere inside React’s internal
There's nothing inherently slow about React internals. It does exactly the work you tell it to do. If you tell it to do too much unnecessary work, it'll happily go do that.
Svelte avoids this by magically not doing the unnecessary work. That doesn't mean it's faster, it just means your design needs to make different kinds of considerations.
I'm going to call shenanigans on this. I've worked on dozens of applications (my own and others) with hundreds of thousands (and even millions) of DOM nodes. I've never seen a problem like this. React simply doesn't rerender things that you don't tell it to. It's easy to try to do something clever and have it cause unnecessary (and expensive) rerenders, but that's not the fault of React internals being slow, it's the fault of the userspace code doing more work than it needs to. Of course the profiler shows the time is spent inside React, it's doing all the work your code told it to do.
React also gives you great tools to avoid and remediate this (memoization wrappers, the dev tools highlight elements that rerender, linters that check you're using hooks correctly), so it's really hard to say that this is a slowness that's endemic to the vdom.
I would be very, very surprised to see React code that can take 300ms in the virtual DOM unless it's deliberately designed to do so. To be honest, I've built a whole lot of stuff in React and it's not even immediately clear to me how I would deliberately design an app to take 300ms in the virtual DOM.
I have used it on production projections and it's seamless. It works with third party React packages with no problems whatsoever and is much lighter. For a stock standard SPA with no need for the upcoming server rendering stuff in React, it's excellent!
Luckily, there is Ember.js, which provides the best developer and user experience for a decade now, with smooth and consistent update and improvements.
Still the best choice for huge, corporate frontend projects.
The fact that it's been well over 3 years since the introduction of hooks in React and people still don't understand how to use them makes me very worried for frontend development.
It's also been well over 3 years of myself and many, many other people and teams competently building extensive web apps with React and hooks, so it's not super clear to me in what sense these people "still don't understand how to use them."
There are many reasonable criticisms of hooks, like 1) some people have a strong personal distaste for them, 2) some educators say they're difficult to teach to newcomers, and 3) many people who use them don't have a deep understanding of how they work and how to handle some of the "gotchas." But none of them lead me to conclude that people "still don't understand how to use them."
The JS ecosystem skews younger, not in age, but in years programming. Most people who maybe want to dabble, or fix a script or something, start with JS or Py, and as most of us do, they enjoy programming and venture out to pick up another language along side it. As is the custom, eventually if they chafe against the limitations of JS enough, they move on to something that improves on those, and their newer contributions go there, whether it be typescript, rust, zig, go, and so on.
Looking back at the JS side where the types don't bite, there's a library for everything, and the logos are really good, what we end up with are a lot of first year, or first three year contributions to the ecosystem, so a lot of patterns and algorithms are wisdom lost and aren't present, a lot of libraries are good but not perfect, and there is really no incentive to really dive in and become an expert on a given framework like react, because any day the community might decide, "It's VUE time now" or some such other technology, and suddenly you're the guy using Gulp and Angular on a busted 2021 macbook at the hackathon and people are addressing you as "sir"
There is no incentive to be "the react guy" when it comes to JS ecosystem, that 15 mins is quick.
I am "the react guy" -- but before that I built knockout applications with requirejs & angular applications with coffeescript and gulp, and before that with a 2000 line long script.js & jquery ;)
I don't really disagree with anything you said, except to point out that a lot of the current crop of web devs have never experienced what it is to build an app without any abstraction such as React, and quite understandably have no idea what problems it is doing for them.
I do know that since hopping onto react with all of that ~baggage~ context, I have never wanted to program UIs with a different model. It is true that hooks introduce a layer of abstraction that is sometimes difficult to reason about, but IMO they boil down the problems we faced with class components/lifecycle/server rendering gotchas, and put them front and center - forcing you to confront and fix them rather than settling for a solution that works 99% of the time.
> the current crop of web devs have never experienced what it is to build an app without any abstraction such as React, and quite understandably have no idea what problems it is doing for them.
This is true. A lot of those struggles that the earlier people established, the patterns and best uses, those are lessons learned the hard way, and the impact on generation 2 is often much less because that wasn't a problem that needed to be overcome.
What give you the impression that people don't get hooks? In our company I see zero questions about how hooks work on slack. 90% of the time useEffect is enough.
Yeah I was going to say.. where’s the confusion? Once you learn the 3 or 4 most used hooks you can pretty much achieve everything you would ever need in a web app. It’s not like they aren’t documented well, and there are tons of videos on them.
> Do they understand how useEffect actually works?
What do you mean? If you're asking if they can and do use useEffect for its intended purpose regularly and don't run into edge cases any more often than with any other coding pattern, then yeah, they understand how useEffect actually works. If you're asking if they have a deep technical understanding of how hooks are implemented in the React library and every potential "gotcha" they might ever run into, then sure, probably not, but the same goes for every basic pattern used in any programming language.
I think the magic of frameworks is that engineers can be productive and create actual value without understanding how things actually work under the hood. In the AngularJS days, how many people could honestly say they understood the scope inheritance and digest cycle entirely? Or how in VueJS that objects' setters and getters are replaced with proxies that trigger a rendering cycle?
Honestly, I would say 90% of professional engineers don't understand how these reactivity models actually work under the hood, and that is okay because it's not strictly necessary to get the job done.
That being said, if I ever had a candidate that is in that remaining % of people who not only know how to use the technology, but what it's doing under the hood (and as a bonus, the benefits and drawbacks of a given approach), it would be an enthusiastic two thumbs up from me.
Hooks are not simple at all, are very error prone, and definitely do not put the user in the "pit of success". JSX is terrific, but React has only gone backwards since classes IMO.
The main gotcha with hooks is declaring dependencies for re-renders. It does not take very long long to learn, and it certainly is simple to fix once the problem is realized.
One of the nicest things about hooks + functional components is how much easier it is to refactor. With class based components, nested state and lifecycle behavior made it sometimes difficult to break down complex components.
But I feel like with functional components and hooks, refactoring into logical subcomponents is a breeze.
I believe that React's hooks implementation is sub-par, not that "a" hooks implementation wouldn't work. Their current implementation badly needs a real compiler (the whole dependencies array stuff is atrocious, as well as some of the useMemo/useCallback performance issues), or a totally different approach that is actually reactive (e.g. SolidJS). IMO the burden of their implementation on the developer is too large.
Not sure what you mean by "really". I guess I have heard some use of that term to mean that it works on watchOS/macOS/iOS/iPadOS/tvOS. In priciple one could create implementations on other platforms, but no one has (beyond toy examples).
Now...that "little" it's-not-a-framework-it's-a-library whose scope is supposed to be confined to just the "view" part of MVC (by which I mean: "React") is inserting it's tentacles (along with a quirky architecture and proven to be error-prone coding patterns, cough, hooks) into the _model_. And it's already corrupted (at least partially) the "controller" part of MVC through React's synthetic events. But hey, at least React doesn't have one of those nasty DSLs, like those Web application libraries/frameworks that use a simple templating syntax on top of plain HTML. Oh wait, JSX is a . . . DSL, and a pretty nasty one at that, given that it's ever-confusing as where JavaScript syntax is legal vs. where HTML is legal; and, oh yeah, the bracketing rules fight each other all day long. But hey, what do you expect when you combine the careful syntax design of JavaScript with the terseness and simplicity of, umm, XML?
So now React's about to ship version 18, which provides functionality which is _roughly comparable_ to version, ummmm, _3_ of Vue.js??? Well, let's see, the React folks have _already, in just a few years_ deprecated (as best I remember): Class-based components, Mix-Ins, Higher-Order Components (HOCs) . . . what's next--let's deprecate hooks?, in favor of, oh I don't know, . . . .sheesh!
Please just STOP now. Wind up the company and give the money back to the investors. (Yes, I know that React is Meta-sponsored FOSS; that last comment was just pure snark...)
On a personal note, I'd like to rebut a comment made elsewhere on this page by '0des': "The JS ecosystem skews younger, not in age, but in years programming." As a counter point, I've been doing Web application development since the early 2000s. Yes, I've worked with Web app. libraries and frameworks that many Front End Developers have not used, or maybe even heard of. Backbone, sure. How about GWT? Or maybe ES4, by which I mean Adobe Flex? How about TIBCO General Interface?
Have you heard of Vitamin C? Trick question--it's not a Web view/app library or even a GUI library--it's a TUI library from the late 1980s/early 1990. Which is to say, I shipped my first front-end code in 1984. And even though I also do full-stack coding, and UX stuff, most of my career has been in front-end coding, both desktop applications AND web applications. Which is to say, at least anecdotally (and possibly egotistically), that I think that I have a pretty well-informed opinions on what makes a good front-end framework.
React impresses me not-at-all; except in a: look-at-me-I'm-so-clever way--now new-and-improved--with extra _functional_ goodness(TM). Conversely, I've scaled Vue.js and Flex applications with so little stress (but with tremendous productivity) that I could laser-focus on the feature set, and on delivering a top-notch user experience.
I need a Web application framework (or at least a View/Rendering mgmt. library) that has these things:
- Strong support for UI componentization
- CSS isolation
- Clean, orthogonal, basic abstractions for change handling and local state management
- Tractable to scale
- M, V, and logic aspects are (easy to) decouple
Also very useful:
- One-way data binding
- Robust, but low-ceremony approach to centralized state management
- Flexible, but type-safe way to marshall/un-marshall data going out/coming in
I'm open to assessing other/new libraries, but the last five years of my career have almost wholly involved coding with Vue.js or React. HOWEVER, I only learned React because I thought that the job market demanded it--not because I ever thought that it was a great library! I'm open to learning other/newer libraries, if they are promising enough, AND if if looks like there is job market demand for them. But the previous version of Vue.js already gave me all five of the needs that I mentioned, above; plus one-way and pseudo two-way data binding. So all I need is Vuex5/Pinia for state management (but I'd be willing to also look at something like a Vue-specific alternative to Tanner's React Query), and there is an increasing wealth of solutions for data marshalling with or without GraphQL.
So, bottom line, in my current job search I am taking a hard line: if React is required, then I'm out. And if you're already using Vue, or are at least open to modernizing your current front-end coding approeach, then I'm interested.
> Now...that "little" it's-not-a-framework-it's-a-library whose scope is supposed to be confined to just the "view" part of MVC (by which I mean: "React") is inserting it's tentacles [...] into the _model_.
useSyncExternalStore doesn't care about what you do in your model. it's for connecting your view to your model in a way that doesn't give you inconsistent states when concurrent-rendering-funkiness happens.
you could even say that useSyncExternalStore exists because React maintainers want people to be able to use whatever state-manager they want (instead of useState/useReducer/whatever) -- uSES was necessary to enable that in 18.
React went from v0.14 to v15. The rationale was the library was stable at that point so they could switch to major versions. Why they went directly to version 15 rather than 1.0 is admittedly a bit odd. I guess it was marketing in the same vein of Java 1.4 to Java 5.
We didn't want a big hubbub around a "1.0" release, since we realized React was effectively already stable at that point and the changes from 14 to 15 were no more dramatic than from 13 to 14 or 12 to 13.
At the same time there's a huge amount of React code at this point. It would be an interesting experiment to try to compile React code to really reactive code that doesn't need virtual DOM.