Hooks are the best thing to happen to React
stackoverflow.blog
stackoverflow.blog
there’s no need to allocate space to a class instance, then call a render() function, you just call the function
That module exports a class, not an instance (not that it was significant). React still allocates vdom nodes at mount, by using sort of new Component for function components and new Hello for class-based ones. It has to store useFoo()-states somewhere anyway.
setName(response.data,name)
That ‘name’ symbol was never defined. Or is it response.data.name?
And the graph shows the same growth rate way before ‘hooks’ were released (2017), which makes “With hooks … has grown” a little non-sequitur. See also the same graph with react-hooks: https://insights.stackoverflow.com/trends?tags=jquery%2Cangu...
In fact, you can use hooks inside of React component subclasses with a little fiddling [1].
The fact that the official react doesn’t support it is basically a design decision. It’s not impossible, but they just want to draw a clear line in the sand about how they think React code should be written.
One of the key red flags I look for in articles like this are whenever the "overhead" of classes is mentioned, such as with "there’s no need to allocate space to a class instance", without any actual metrics showing what this "space overhead" or "performance penalty" amounts to.
It seems like more of a misconception about how JavaScript works. There may be some overhead, but do we know what it actually is in a real world application? Or are we just going on assumptions about what we feel is "light" versus "heavy", often frequently with a biased opinion because we simply don't like OOP concepts?
In short classes execute very fast in V8 jsVM for similar reasons they benefit lower level languages like C++. That improvement applies to the syntax only and almost exclusively to micro benchmarks. The problem with classes, and OOP in general, is that there is a lot of decoration that goes into the code in real world use cases. That decoration is generally not beneficial to performance, such as: getters, setters, a bunch of unnecessary implicit reference calls, access to the DOM using a bunch of unnecessary abstraction layers, and so forth. If you are the kind of JS expert that is easily capable of not using OOP you can probably get this right and make really fast use of classes, but that’s not the primary audience.
https://v3.vuejs.org/guide/composition-api-introduction.html...
Most of these low quality blogs are written by IT students who aren't top of their class and want to stand out in some other way.
Honestly, most articles on the stackoverflow blog feel like that. They are usually shallow. See for example the recent articles about design patterns https://stackoverflow.blog/2021/10/13/why-solve-a-problem-tw... and REST APIs https://stackoverflow.blog/2020/03/02/best-practices-for-res....
const [Name, setName] = useState("")
But yes he got the capitalisation wrong.Proceeds to pass an empty array for no apparent reason* to a function in the first example. The later explanation completely ignores mutability and what it means for the type of value you can use as a dependency.
Hooks are nice for small components, but overall haven’t improved DX over classes much, and introduced a ton of new concerns. Yes, it makes composition easier. It also makes it harder to reason about component lifecycle when state changes are distributed among a dozen little functions with slightly different update semantics.
* I am familiar with how hooks work, am highlighting the implicit complexity
Composition itself is a huge DX improvement.
> It also makes it harder to reason about component lifecycle.
I would argue that it is the same if not better than class based components. One of the amazing things about hooks is that you can ignore all of the hook updates and just treat the values returned by them static to see how the rendering will happen at a particular instance.
For complicated states where you need 4+ hooks - well in that case most of the time refactoring is needed but there are cases where it should not be refactored and I have a certainty something could be done much easier with the old class based way of doing it, but then I would have to deal with people in PR who say we must use hooks for everything.
Recently at my job we had a senior engineer (me), a lead engineer, and a junior engineer (all of us familiar with how hooks work), looking at a 10-line hook in the codebase, and we couldn't collectively figure out whether or not it actually did anything without running it to see how it behaved.
If that's not an indictment of hooks, I don't know what is.
I'm no advocate of OOP, but React components are one of the few places where I've had a very good experience using classes
While hooks do have problems, this could be said about pretty much any programming language or framework, especially when it comes to code that allows for better reusability. In this instance, I suspect it's more of an indictment of the original programmer instead of hooks.
But that's just it: the nuanced ways that different hooks do and don't trigger updates are at the heart of what makes them hard to use correctly.
> Hooks are nice for small components, but overall haven’t improved DX over classes much
I think one of the best things about the transition to functional components and hooks is that you’re strongly encouraged to make ALL components small components. Class components tend to accumulate more and more functionality, which leads to interleaved logic and unmanageable complexity. If you keep all your components small, then functional components and hooks are better than class components, in my opinion. I admit, it can be a challenge to find the right patterns to build up complex functionality out of small, composable components, though.
I use surplus everywhere I'm allowed to, and it rarely bites me. It also allows me to develop pretty robust frontends quite quickly. The learning curve is a bit high, admittedly, but I think that's fine.
sure, looks successful.
> With hooks, it’s now simpler to learn React; because of this, its use in the tech world today has grown: (graph) - proving that its general usage has increased since the introduction of hooks.
wait what? You may as well say that React googling has caused Covid deaths. You could just as easily (mis)interpret the statistic to claim hooks have made it harder - "as per the graph, now they need to ask on Stack Overflow for things that were previously obvious". Pseudo-statistical garbage.
> Pseudo-statistical garbage
Seems like the author is just making a relatively benign, and half-hearted, conclusion (that React has gotten more popular) based on the data available. When I hear more people talk about something, that generally leads me to believe more people care about that thing. Does it have a P value that a journal would accept? No. But this is an opinion piece about a cool tool the author likes, not a paper on a new drug. You're going a little off the rails.
But yeah, harsh words by akdor1154 maybe but I agree with the sentiment in general.
You're not wrong, thanks for chill response.
More concise? It seems about identical to me.
More straightforward, easier to understand? I guess it's subjective? Defintiely not obvious to me, but I do come from an OO background.
easier to learn for beginners, as the article next says? Does anyone have any even anecdotal information on that, if they themselves are a beginner or know some?
It's really not obvious to me. I mean, these things are subjective (except for "concise" maybe), if lots of people are finding it more straightforward, easier to understand, easier to learn, then they are... but are they?
I had the exact same reaction to this code block. Using componentDidMount() made it clear what it was doing. Reading the functional version I have no idea how useEffect() equates to componentDidMount().
I am also from an OO background and am currently working on a very large Angular 12 project on the front-end with a C# back-end. I am really enjoying working with Angular ... I can quickly see at a glance what is going on and everything is structured and well organized. React often seems to be targeting developers coming from the "other direction", with a JS background first.
I also don't understand "hooks have made React easier to learn for beginners". My mind glazes over sometimes reading these various examples and I've been in the industry for decades.
To each their own I suppose.
OO lifecycle: constructor, render, componendDidMount, render, (render, componentDidUpdate, render)*
FH lifecycle: (renderFn, useEffectFn | (useLayoutEffectFn, renderFn))+
For more in-depth musing over the exact behavior, see, for example: https://reacttraining.com/blog/useEffect-is-not-the-new-comp...
I'm not satisfied with what I see in my current project at work, but that may be because of Windows and Windows Defender.
There is no equivalent of lifecycle in hooks! Sure you can give some example of how to produce a componentDidMount, but that defeats the point of hooks.
The hooks api is designed to focus on dependencies and what to do in case a dependency changes. When there is an empty array as a dependency , that implies that this hook will never rerun as it has no dependency. Similarly, if there is one dependency, the hook will rerun when that dependency changes.
Maybe beginners these days are not much of a beginner at all ;)
Any idea how useState accomplishes the mutation of the n-1 element mechanically? Seems to violate the by-value and by-reference rules in my mental model of how javascript works.
I was originally guessing it was a syntax trick of destructuring where it’s actually calling some kind of property accessor behind the scenes even if you don’t type the parentheses. But the article says this also works so now I don’t know…
const loadingTuple = React.useState(true)
const loading = loadingTuple[0]
const setLoading = loadingTuple[1]
loading // true
setLoading(false)
loading // false
(Edited to fix code formatting)Then I discovered hooks and everything clicked. Now I can practically write React with my eyes closed.
Another thing that is not straightforward at all is to know when it will re-render children components and when it will not. I had to go through several blog posts to begin to understand it.
Then all that extra complexity starts sucking more when you figure out that an old server-rendering framework would work just fine for the app you are developing, it didn’t have to be a SPA in the first place. But React was chosen just because it’s the cool modern framework.
I'm sad I cannot upvote this more than once.
One really important aspect here is that Hooks don't just replace class components, they also replace the need for higher order components in many cases. And they make it much easier to compose stateful behaviour in React components.
The magic (and the learning curve, which I think is where much of the pushback on hooks comes from) is worth it.
On the one hand, it really does clean some things up well, like for higher order components, but also I find the magic just leaks more than I’d like, and as a result I feel like the only way use the magic safely is to study all of the pitfalls.
It’s like using C++ smart pointers but a bit less foot-shooty.
What is a bit tricky is useEffect, but I found that in many cases I either didn't actually need it, or I can use libraries for common tasks that hide the more complex parts. Using react query for data fetching simplified those parts considerably, and I never have to write a useEffect to fetch data from the server.
I must correct this. Pay attention to this function [0]
It's just a plain linked list and nothing is being fired off by iterating through the linked list. When you call a hook react either creates a new node in the linked list or it pops the current head off the linked list so that the second hook will get the second element, the third hook the third element and so on.
The hooks are fired off by calling them. React isn't calling them again. Each render simply calls your component function and your function calls the hooks which runs a reducer [1] on the internal queue of the hook to compute the latest state. The second return value is just pushing elements into the internal hook queue.
[0] https://github.com/facebook/react/blob/e07039bb61e3d006ad552...
[1] https://github.com/facebook/react/blob/e07039bb61e3d006ad552...
The code isn't simple but the way it works is much simpler than what these blogs show.
1. React uses a bunch of global variables during rendering to make the current component and its hooks available to a global function (useReducer). Basically a react specific "this"
2. Hooks are stored in a linked list, meaning the first hook that is called gets the first hook's data. If you have hooks A,B,C and disable B then C will get B's data.
3. The way hooks themselves work is that all hooks call useReducer and the hook state is just a queue that is fed into the reducer which is called whenever useReducer is returning the latest state as the first returned value.
4. The second returned value is just a function that pushes a new entry into the queue.
But they aren't really magic.. I think the rules of hooks makes it pretty easy to guess how hooks might work under the hood. The fact that they need to be called in the same order every time indicates to me that the could, perhaps, be some sort of hidden array attached to each component instance that keeps track of whatever the hook needs. Each hook call increments the hidden index it accesses by one.
It's a bit odd from the perspective of normal Javascript, that's for sure, but we're already pretty far from that because we're working within the React runtime.
When I started using React I got used to knowing at which point in the lifecycle some things would run, and hooks threw a wrench in that.
Took me several months to actually get used to the way things work now, but lately I've found myself not even thinking about those things any more, which would mean the entire matter of lifecycle was removed from my problem space in such a tidy and neat way I find that amazing.
I'd love to see some more transpiling magic around them though, most of the bundled hooks have clear correlations to plain JS and it would be pretty cool if the same dev stacks we use could let me write plainer JS and handle that wrinkle for me too.
const loadingTuple = React.useState(true)
const loading = loadingTuple[0]
const setLoading = loadingTuple[1]
I've never seen this construct once in the wild. Makes one wonder if the author actually uses React.Just wanted to share this on how the simple looking hooks can hide some really hard to find bugs.
It saves all the dependencies to avoid recomputing if the same dependency shows up any time in future.
But in my case I was using an old school library where you call destroy and set reference to the instance as null, and putting that as a dependency in usMemo was a bad idea.
Class components were fine. Hooks are also fine.
They both give you plenty of rope to hang yourself with. And if a component in either form is hard to understand it’s probably just too f big.
What bother me most is those people that have no clue how hooks work, but claim their “functional” components are now faster, because they’ve read that functional is good somewhere.
[0]http://raganwald.com/2016/07/20/prefer-composition-to-inheri...
Everything that is possible with programming has been possible since we were poking hex codes directly into memory. All language improvements have been about semantics, or fixing performance degradations brought about by new programming paradigms.
[edit] I do agree that mixins could've been good too.
Hooks feel more like something added to fill in space and another example of the ecosystem bloating itself just for show.
But it's not "the best things to happen to react". It added plenty of complexity to the system.
Hooks have been embraced by the community in a way that now many features only exist as hooks, which makes ones life difficult as soon as you don't want to use a functional component.
An no, not everything should be a functional component.
In fact, some things are really hard to do with hooks. I dare you to craft a simple reusable _and correct_ useInterval() hook without stealing the solution online.
Go on, I'll wait.
function useInterval(cb, ms, deps) {
useEffect(() => {
const id = setInterval(cb, ms)
return () => cancelInterval(id)
}, [ms, …deps])
}
I think this is much nicer than having interval maintenance and prop diffing is didMount, didUpdate, and willUnmount. Certainly it’s easier to get the hooks right (for me) and all the logic ends up in contiguous lines of code. It’s a higher level interface so it takes a bit more practice to apply - feels like Ocaml more than Java.Dan Abramov has a good post that explains the issue here well: https://overreacted.io/making-setinterval-declarative-with-r...
That's the whole problem with hooks: when things are easy, they are nice. When things are complicated, unfortunately, they look easy, and you start to think you understand what's going on. But most people, me included, don't.
That’s fair. The other side of the coin is that class components have their own kinds of failure modes. I have fixed a lot of buggy class components that simply forget that props change - they do some kind of effect-y thing using the first props during didMount, and then never respond to prop changes aside from `render()`. With hook components, the linter will get mad about that sort of thing. In either case there’s gotchas, but I like that useEffect helps people avoid leaking and think about changing props.
I've seen people reimplement it wrong in my codebase more than once, whereas no one gets useInterval incorrect because.. we can actually share that functionality across components now.
Why do react guys choose to fight these battles in the first place? We had a clear model, MVC (not web 1.0 mvc which left MC at the server-side, I mean fully-in-app one). All these setups and teardowns were done in “C” part, which didn’t depend on how exactly views are [re]arranged on the screen. And all interactions went through C, leaving little to no state in V (only very local ones, e.g. whether a combobox shows a popup currently, or is there a div-tooltip above a label). Then react broke in and told us that all we have is views/components and got a grip on their lifetimes, forcing us to jump through these hoops.
And that functional-async thing was not even the reason for that. React could have the same “off-screen” controllers with explicit lifetimes and setState() and friends, to be more testable and composable and all of that. But they chose to push all state to transient objects and then began to heroically solve the mess created by themselves in the first place.
My current company, for example, enforces a pretty strict architecture where core application state lives "outside" of React [0]. What page we're on in our SPA, for example, is determined by state in our page store. We don't explicitly rely on component mounts to fetch data - instead, when we switch to a certain page via some action (like OpenUserPage), and _that_ fetches the data needed for that page. When we navigate away from the page, we call CloseUserPage, which unloads that data.
We try to push all business logic into discrete use case based actions. Like you mention, we use local component state strictly for view state (e.g. is this menu open?). We try to use hooks like useInterval sparingly (I think we only use it for things like updating relative timestamps every minute). We do use setInterval in business logic though, where it's simpler to reason about, for stuff like polling.
In an ideal world, there'd be very little need for complex hooks, where most components are strictly function components that only re-render when state that they're subscribed to in the store changes. But the real world is messier, and making a beautiful SPA with animations and whatnot often requires tricky view state logic with hooks. And of course, you have to be careful to not re-render components unnecessarily in performance critical situations where you're rendering a lot of stuff.
But I think the sort of high level separation you're describing can and does exist, and IMO striving for that separation makes it much easier to understand how data flows through the app, and obviates the need of digging into component lifecycles to manage the state of the app.
[0] At the end of the day, it does live in a top level context, but we don't interact with that context directly from child components.
you have to be careful to not re-render components unnecessarily in performance critical situations where you're rendering a lot of stuff
Interestingly mithril’s author, the library that I use in place of react, suggested that for humongous doms it’s better to split it into sub-apps. In short, you make a shallow component that mounts another entire “app” into its div on its mount, so that updates do not propagate downwards (the main app simply doesn’t know about subapps). Otoh, events propagate as expected due to regular dom. Personally I find mithril’s approach more outside-state compatible, if you don’t do funny tricks with event bubbling. If you’re not familiar with it, it’s the same hyperscript/vdom, but re-render()s everything after any event related to dom it controls, unless you tell it not to in a handler. That is fully out-of-your-way approach compatible with anything. Animations, jquery components, you name it. I don’t get why people choose react, which seems to be the opposite of that. What do you think I’m missing here?
And thanks for your time and the expanded reply!
Edit: frankly, I wouldn’t even bother with react and would just use what fits, but its presence is overwhelming in a sense of both community and job options. If there is a way to use it without abstraction pain, I’d like to understand it.
Most state libraries provide a mechanism by which you can "connect" components to parts of the state you care about, so that they automatically re-render when that part of the state changes. New props come in, and the components can decide what to do with them.
> Or at least have state to be duplicated in them. I mean, if a control/table gets a mutating event, you have to update both outside state and internal setState({input}) as well, otherwise input wouldn’t appear. And when outside state changes by itself, there should be a way to forceUpdate() with props or maybe a direct setState(). Am I right?
So this is where things get murky, regardless of what library you're using.
In the application I work on, our stores generally reflect the source of truth in the backend, and it's real-time so the stores can update literally whenever. This puts two-way data binding you see in other libraries out of the picture. So most of our forms use local state, because we only want the data store to be updated with the final result the end user actually saves. Interaction wise, it wouldn't make sense to submit it anyways until the user blurs it or explicitly saves it (plus it would be unnecessarily expensive to constantly be saving to the backend every keystroke).
And by keeping the state local, if an update comes in from the backend on the entity we're editing in the form, we can notify you that "Hey, someone else updated this!" while still keeping our form state, and letting us make the decision as to whether we want to overwrite it. But we could also choose to just override the form state if we so choose.. we could just look at the "connect"ed state we care about, and if it changes, overwrite the local state in the form. That's generally not desirable though from an interaction perspective.
> In short, you make a shallow component that mounts another entire “app” into its div on its mount, so that updates do not propagate downwards
We do the same thing in our application. Although the top level component technically re-renders when our data store changes (because it's stored in a top level context component), we just have memoized components beneath that take 0 props, which therefore don't re-render even when the parent re-renders.
And we re-use this same pattern through the application, creating separated subtrees that only re-render when data they care about changes.
So I think of our application like this: either user interaction occurs (like a form save), or the backend updates. This changes our data store (which tries to reflect the state on our backend). This update causes the components that care about that specific piece of information to re-render. Their children may choose to re-render or not. And then we wait for more interaction / backend changes. This is where the notion of "unidirectional data flow" comes from - it's basically just a tight one-way loop that drives the presentation of our application. Forms do muck this up a bit with their local state, but that's something I think _every_ application has to think about handling
I will admit I only learned frontend development / React properly at my current company (previous used a messy Backbone.js app), so there might be simpler / better ways at this point, but I find it very easy to reason about our application as a result of this pretty explicit data flow + our architectural constraints.
- lifecycle methods are clearly named and explicit. Hook lifecycle is implicit and you better have the whole doc in your head. It depends on this weird array, and have a few edge cases.
- you need useRef() to hold the interval ID for it to work with hooks. A fact you are unlikely to realize without a hard debugging session. While a simple attribute will suffice with a class.
- spreading accross methods is actually nice: people intertwine code for various steps easily in hooks, making a mess of things
The benefit of the hook version, of course, that the reusable version is very nice.
> Hook lifecycle is implicit and you better have the whole doc in your head. It depends on this weird array, and have a few edge cases.
useEffect() will run after each render if you don't specify a dependency array. Otherwise, it will run only if any dependency in the array has === changed. That's pretty explicit to me (but I think you mean in terms of naming.. but I think one or two reads through the docs should make it explicit).
The return "cleanup" function is a bit odd at first, sure, but it's really just a function that will run _before_ the next effect runs.
The general recommendation is to not use the dependencies array at all unless necessary.
> you need useRef() to hold the interval ID for it to work with hooks. A fact you are unlikely to realize without a hard debugging session. While a simple attribute will suffice with a class.
useRef() in practice behaves almost exactly the same way as instance properties on class-based components, so I think of them as the same thing. useRef is even nicer for debugging IMO because their values are exposed in the React devtools, unlike instance properties.
> spreading accross methods is actually nice: people intertwine code for various steps easily in hooks, making a mess of things
I think having 5 features crammed into the same lifecycle method is far more of a mess than each feature handling their lifecycles independently. That's the point of hooks: you can wrap up functionality that's related to the same feature all in one place. IMO that's simpler to reason about and debug, but I can understand how that might trip other people up. It's sort of horizontal vs. vertical.
Hooks as they are have some non-obvious rules which can make them hard to understand / learn. Also debugging is not great (but that's true for react in general, imo)
Also, that loading boolean seems unusually variable for a constant.
I see this said a lot, so what's really going on that the class API obfuscates?
Like any other constant, it never changes during the lifetime of its scope (a single function invocation) but can be different during different invocations. (The initialization by the useState function, though, is somewhat magical, involving external knowledge of whether this invocation is rendering a new instance or rerendering an existing one.)
loading // true
setLoading(false)
loading // false
they mean that it will become false in the next invocation of the render function, I hope?Very similar API to React but the reactivity system is nicer. There's no hooks rules like not putting them in if statements or inside other hooks.
Very cool and I think this is the future. It also helps that the performance is crazy good and it doesn't re-render everything all of the time like React.
This makes everything is so easier:
const data = useQuery('/api/user/:id', {id:1}); return (<span>{data.name}</span>);
No error handling, not loaders, just one line.
An underrated benefit is that you can depend on data in other hooks in the same component. If you have an error or loading bail-out it makes hooks calls after it conditional.
Suspense hasn’t really taken off yet because it’s not quite fully baked, and thus very few libraries are starting to take advantage of it yet. Once we get stale-while-waiting and server-side suspense, things are going to change rapidly.
My gut feeling is that the same will happen to React in the next 5 years, possibly as a result of more native support for web components and front-end state management. I was writing Visual C++ front-ends around the time jQuery first appeared. Front-end state management should not be this complicated.
Prior to React, we were using Backbone, Handlebars and jQuery. It was horrible. Logic was put into Handlebars templates, Backbone models created an unnecessary abstraction, we had to setup our own event handlers for every model -> view change. It just didn't work. JSX solved all these problems.
Frontend-state management with React is not complicated. There is a current trend to trash React because of its perceived complexity. Take it from an experienced React developer- it's easy and fun. Hooks are a short syntactic sugar over its lifecycle methods. Once you learn hooks, there isn't much more to learn.
I'm not sure where you are in your career now, but I recommend that you give React another chance, and avoid jumping on the bandwagon to hate on React. It's solved so many problems for me and allowed me to actually enjoy my job as a software developer.
It's just different styles of writing code that does the same thing.
If you use React often, you can build anything, really fast, using either paradigm.
Yes, hooks are nice. Classes are still nice.
Either way, I hope you’ll join me in flagging it.
Hooks seem to be fine for dumb little UI bits that you'd use directives for in AngularJS.
Swiftui was heavily inspired by React (https://twitter.com/jckarter/status/1135666944273571840)
Flutter and Jetpack Compose - also admittedly. React Native is a given.
I think the biggest change that React has wrought is the paradigm of how UI can be composed...versus the javascript library itself
Generally, it seems like it. But I'm definitely not taking an authoritative stand here!
Here in Bulgaria these jobs pay about 30% more than identical Vue positions. It's very unfortunate for people like me
If you put a hook in your functional component, it's not functional anymore.
The whole point of functional code is that I'm minimizing state, and writing code that's pure - It will always do the same thing given the same inputs.
And that whole guarantee really gets thrown out the second you embed a hook in the function.
Now, that said - I also prefer react with hooks. To me they compromised on the push for functional code in order to make working with side effects and state easier.
Hooks feel a lot nicer than having to wrap a pure component in a HOC to get the same effect - but it lulls folks into complacency because they are actually dealing with side-effects, and now the boundary is a lot murkier, and comes with a completely different (and not particularly intuitive) set of rules around how to safely use hooks.
it's a variable inside a function -- how am I supposed to test its state from outside (ie. test runner), without resorting to ugly hacks?
And if you want to test the component using it, you simply mock the hook and make sure the mocked hook returns all the value you want to test. What goes inside of the hook is outside your concern when testing the parent component.
The main point of fuctional paradigm in engineering is composition over inheritance. Hooks give us that, and a way to escape classes (and people who really want to write JS like Java).
This isn't my goal with functional programming. My goal is to minimize side effects.
I want code that is repeatable, consistent, and easy to test. I want front end code that's as close to a pure projection of state->DOM as I can get, because that makes all sorts of problems simply vanish (And the less state, the better!).
I like hooks, mostly - but lets not pretend here... they're only functional if you don't look at the man behind the curtain.
It's a feature I was missing in react nearly from the strart and I was trying many times to mimic it with varying degree of success but all attempts were finicky.
not thinking about loading states hurts ux and perceived performance
edit to clarify: i like hooks (though people abuse them) and believe react has generally always been best-in-class at thoughtful interface design. suspense breaks with that imo
I'm not following. Can you just wrap the component that suspends with <Suspense fallback={SharedLoadingUI} /> ? The SharedLoadingUI could help with duplication right ?
Unfortunately, SPAs are often used where a classic SSR page would suffice, all in the name of "performance optimizations" and whatnot. Meanwhile, UX suffers because often edge cases like slow/spotty connections, device sleep or even just accessibility or not considered at all.
Maybe everyone on HN despises SPAs because we've all seen how fast, light and reliable HN itself is compared to e.g. Twitter where clicking anything usually brings my computer to a crawl and half the time shows a loading error.
"Hooks are functions that let you “hook into” React state and lifecycle features from function components." and "[hook] adds the ability to perform side effects from a function component. It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in React classes". Quotes from React docs.