Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Hooks are the best thing to happen to React (stackoverflow.blog)
95 points by nsoonhui on Oct 23, 2021 | hide | past | favorite | 134 comments


Is that a high-quality article? It feels like yet another clueless hype with arguments stretched to make a point.

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


I don’t really understand the authors reasoning either. Classes in JavaScript are just sugar around functional closures.

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.

1. https://www.npmjs.com/package/react-universal-hooks


> Classes in JavaScript are just sugar around functional closures.

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?


Performance and classes are a complicated subject.

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.


> Is that a high-quality article? It feels like yet another clueless hype with arguments stretched to make a point.

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


Yeah, the arguments in this article are not the arguments why hooks are better than classes, or why they exist at all. There is a reason though. I actually think the Vue docs explain it quite well (should be pretty easy to translate to React):

https://v3.vuejs.org/guide/composition-api-introduction.html...


    const [Name, setName] = useState("") 
But yes he got the capitalisation wrong.


as soon as I saw it's a SO article i closed it. I have gone through enough low quality SO articles and podcasts to understand that they write these for SEO not devs.


not SEO but recruiters and name recognition.

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.


I wish these people would go back to collecting those useless developer certificates. At least they didn't pollute search results with their low-effort blogspam back then.


I was also put off by the author not being able to make the distinction between a library and a framework. The two are almost the inverse of each other: a framework is an application scaffolding while libraries are the building blocks that fit into said scaffolding.


It's not an article. It's more like "content marketing".


“hooks make for more straightforward and concise code”, “easier to learn”

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


> Hooks are nice for small components, but overall haven’t improved DX over classes much, and introduced a ton of new concerns.

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 simple states hooks are the same if not better, I would say almost probably better.

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.


Agreed. I'm a senior dev and I think they're a nightmare to follow, even once you understand how they work. The combinatorial complexity of all the different implicit rules is astounding, and there's an entire armory of foot-guns. The lint rules help, but are intrinsically limited by working at a syntax level, not a value level; it's very easy for things to slip past them.

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


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

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.


It wasn't super elaborate or poorly-written, though it was kind of subtle. It had to do with letting you do something without triggering an update. What we couldn't tell is whether or not it was having the desired effect (or any effect at all) on component updates.

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.


I agree with your concerns, but about this…

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


You can put as much JSX in class components as functional ones. I've seen both of them being used to fit as much JSX as humanly possible, I don't think it matters at all which one is being used as the implementation doesn't care how much you put in it.


There’s a pattern I’ve seen in a lot of class components that doesn’t fit as well in a functional component: every time the component needs to render some new bit of JSX, create renderThing(), renderOtherThing(), etc, ad infinitum. These just don’t happen in functional components. Technically, you could create an inner function, but people just don’t seem to do it as much. They create a new component. Or at least, in my team, the resistance to breaking that stuff out to separate small components was much lower than with class components. It took very little effort in code reviews to push people towards small components and do it consistently.


Check out Surplus. It was "hooks" before the react team decided to implement a half-assed version of clocks.

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.


For the same reason some many frontend developers refuse to use typescript because "it adds bloat to the code". And I have to admit, if your code is mostly concerned with checking if form fields are not empty before submitting, then you are probably fine with unstructured random chunks of code. I think it's parallel to Pythons success in certain niches. But once you start building real application running in browser (or mobile, or even desktop these days, hello Teams), then pretending that untyped, unstructured and functional (if you even know what that means) is the best thing that happened since Touring then you're heading for a huge disaster.


> The graph above shows the increase in the percentage of Stack Overflow questions about React over the years vs. other popular JavaScript frameworks,

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.


> sure, looks successful

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


The conclusion the author makes ("With hooks, it’s now simpler to learn React; because of this, its use in the tech world today has grown") is pretty garbage though, could be any reason popularity of the search term has increased. That it's because it's easier to learn feels pretty far down the list of possible reasons. The rate of the increase for React in that graph is pretty constant, and I see nothing that gives the impression that the introduction of Hooks (February 06, 2019) accelerated the increase.

But yeah, harsh words by akdor1154 maybe but I agree with the sentiment in general.


That's fair. I'm probably biased because I was introduced to react with redux and absolutely hated it. I dove into hooks the moment I had a chance and found it way easier, so I am exactly the anecdote the author thinks is the majority :P


> You're going a little off the rails

You're not wrong, thanks for chill response.


> The code above collects user data by using the Axios API and prints it on the DOM. The useEffect and useState hooks make for more straightforward and concise code that is easy to understand and work on than the class components

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?


> More straightforward, easier to understand? I guess it's subjective? Defintiely not obvious to me, but I do come from an OO background.

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.


I think the problem is (not in particularly you) that people want apples to apples comparison.

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.


Not sure this has much to do with OO vs Functional Hooks, more like reading the docs?

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


Just out of curiosity, about your Angular project, how large is "very large" in terms of component, module count? How long does it take to build?

I'm not satisfied with what I see in my current project at work, but that may be because of Windows and Windows Defender.


I think hooks are harder to learn for beginners, but better allow advanced users to separate concerns.


Yeah, the current docs don’t do a great job - I’ve felt like I need to write a really use-case tailored migration guide for my teammates who didn’t pick it up easily by osmosis. Luckily it looks like the React team is hard at work on a new set of docs that does a better job!

https://beta.reactjs.org/


Even though I'm not a beginner now, I could guess, seeing an array that contains a magic function at its nth index (useState), which could mutate n-1 th index, would have baffled the life out of me.

Maybe beginners these days are not much of a beginner at all ;)


Yes, my feelings exactly. :-)

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)


Ah, the code example doesn't work. User ptx clarified in a separate comment that the author meant it as just psuedo-code; it's value will be different in the next function invocation: https://news.ycombinator.com/item?id=28969530


setLoading just pushes an element into a queue. Calling useState will create the queue and fill it with true as initial state during the first call and then run a reducer on the entire queue to calculate the latest state. Simply calling setLoading does not modify loading. When you push an update to a queue react will rerender the component and thereby call useState again which runs the reducer which returns the latest state.

https://news.ycombinator.com/item?id=28972892


I’m not a beginner using React any more. But useEffect is not straightforward at all when compared to the old methods (disclaimer: I also have a OO background).

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.


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


I was a beginner with React a couple years ago and things like Class components were Greek to me.

Then I discovered hooks and everything clicked. Now I can practically write React with my eyes closed.


Hooks are more magic than I prefer, they behave in an unusual way compared to normal functions. But overall I like React more with Hooks compared to the previous state.

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 is usually what gets to me. Stuff like useState depending on the order it was called under the hood, meaning you have to use it a specific way.

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.


I have seen a lot of crappy blogs that try to explain how useState works and they were all highly misleading. They try to explain things with closures and other random made up stuff.

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.


While this is kinda weird, I didn't find the dependence on the order of the hooks problematic. The most natural way to use hooks is also the correct one, at least for me.

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.


oh wow, if pure functions become hidden-order dependant again then the way is broken.


First of all: useState is not a pure function, nor is setState. I would think that’s obvious from the name. The order isn’t hidden and it’s not magic, it’s the order they’re defined in the function body, because under the hood the React runtime is storing all the hooks you call in an array and firing them off by iterating through the array. It is a very conventional architecture in event handlers: of course there is going to be an order they get called in, there is no way to avoid it. But it’s perfectly explicit.


>because under the hood the React runtime is storing all the hooks you call in an array and firing them off by iterating through the array.

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


Yes, you're right. Iterating is not the right word, and it's the ordering of the data in the internal queue that I was thinking of, which is fixed by the order in which the hooks are called. During rendering, your component itself will run through the queue. It's not React doing it.


Using hooks to integrate 3rd party libraries is so much cleaner than using HOCs.

The magic (and the learning curve, which I think is where much of the pushback on hooks comes from) is worth it.


> Hooks are more magic than I prefer

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.



Hooks -React Fiber actually- really messed with my spatial awareness of components.

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.


That‘s how I think of hooks. They don‘t let you express the lifecycle of things, only the data dependency graph. React moved the level of abstraction a second time, and I think they succeeded.


You still need to understand lifecycle of a component to understand how useEffect() works though...


Since switching to React functional components I haven't once felt a need to switch back to class based components. The hook ecosystem just feels right. I suppose it's a matter of opinion at this point, but I do really appreciate the hook feature of React.


Same here, writing custom books is also a really clean way to make components less cluttered.


I share this vision. React functional components coding experience is amazing..


    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.


Maybe they're using ES5 rather than ES6+?


I once saw someone writing ES5 barehanded, like savages.


I was debugging my apps monstrous memory leak. It took me quite a while to figure out that ‘useMemo’ was sneakily responsible for it. Currently the way this hook works is that it will hold references to all the objects that go through it. In most cases these objects are pretty small and trivially contribute to a leak, but in my case I was saving a giant class instance which was causing significant memory usage.

Just wanted to share this on how the simple looking hooks can hide some really hard to find bugs.


Do you mean that it holds references to all versions of the memo’s dependency array AND all versions of the memo return value, or just that it holds the last version?


It holds all of the references to the dependencies strongly, which makes sense since it is a memorization helper function.

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.


I’m getting really tired of these distictions people make, saying either class components or hooks are better.

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.


The benefit hooks bring is that as well as already being able to separate out components, you can now separate out React functionality into reusable pieces in a clean way.


I'm still not sure why ostensibly little effort was put into figuring out mixins[0]

[0]http://raganwald.com/2016/07/20/prefer-composition-to-inheri...


It's not about making something possible; it hasn't introduced any new capabilities. It's about improved semantics.

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.


For me (and I suspect a few other people working with react, but certainly not everyone), the main issue with hooks is exactly that class components were fine, and hooks are fine, and we did not need nor want more "fine".

Hooks feel more like something added to fill in space and another example of the ecosystem bloating itself just for show.


There’s things that are easier with hooks, but there’s also a lot that has become a bunch more magic.


I hates the idea of hooks. After trying them, there are things are like about them, and I use them.

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.


I think your exercise framing is silly but I’ll bite since I’m a programming addict. Writing on my phone so forgive any typos.

    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.


I think the original comment is fair, and comparing your attempt to working solutions online will reveal the complexity of bugs that can arise from hooks.


I see - the issue with my solution is that the interval will reset whenever the deps array changes, starting over the delay? The implicit "state" of the time left before the next tick is destroyed when the effect re-runs - instead this should persist.

Dan Abramov has a good post that explains the issue here well: https://overreacted.io/making-setinterval-declarative-with-r...


Also the id must be hold using useRef().

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.


I don't think you need to hold id using useRef(). The cleanup callback holds the id from the same render, so it will clean up the correct interval.


> 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 don't find the correct class component implementation to be any simpler. You have to worry about the exact same issues (clearing on unmount, potentially canceling + calling again if a prop dependency changes), except now the implementation is spread across multiple lifecycle methods.

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.


As I read this thread I feel helpless and not sure where to ask it, so excuse me that it is after your comment.

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.


I think I get what you mean (lmk if I don't). But there's a reason why libraries like redux can exist: React is _not_ a framework and doesn't impose many restrictions on how you choose to structure your application.

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.


But afaiu, with state living outside of react, components have to be updated by hand? 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? At least MobX does exactly that behind its fancy object properties. I mean, I’m not saying react is restrictive, it’s just tedious and doesn’t even try to help if you claim ownership over your state.

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.


> components have to be updated by hand

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.


Yes but:

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


Fair disclosure: I'm definitely biased towards hooks because I feel like I "get" them and would never go back. So bear with me

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


To me React hooks look like they want to be a different programming language while still using js-syntax. It would be great if "useState", "useMemo" and the like would be first class citizens in a PL.

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)


You should check out Redux if React seems alien in its own language. This entire stack pretends to be something non-imperative javascript, but at the end of the day just offloads all the burden of writing few layers of abstractions by hand on developers.


I'm only vaguely familiar with React, but the class-based components and their methods make perfect sense to me after seeing the diagram and example in the article, whereas I have no idea how the "more straightforward" and "easy to understand" hooks-based example is supposed to work.

Also, that loading boolean seems unusually variable for a constant.


> Also, that loading boolean seems unusually variable for a constant.

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


So where the article says

  loading // true
  setLoading(false)
  loading // false
they mean that it will become false in the next invocation of the render function, I hope?


That's exactly what the setter function returned by useState does, it sets the value returned the next time the corresponding useState is run for the same component (it is order based, which is why hooks must be run in a constant order.)


They basically invented a new programming paradigm. You’re not familiar with it so it doesn’t make sense. But it actually more closely matches what’s really going on, so it’s more straightforward to someone familiar with how React works. The class-based API did not closely match how React actually works and so led to various common bugs which don’t occur anymore with the hooks API. You do have to learn the paradigm, but having done that you understand a lot more what you’re doing. This would probably be clearer if it were just a new programming language instead of being shoehorned into Javascript—then it wouldn’t seem so weird to be learning a new paradigm.


> But it actually more closely matches what’s really going on, so it’s more straightforward to someone familiar with how React works. The class-based API did not closely match how React actually works and so led to various common bugs which don’t occur anymore with the hooks API.

I see this said a lot, so what's really going on that the class API obfuscates?


It isn’t perfect but this is probably the best explanation that I know of https://overreacted.io/a-complete-guide-to-useeffect/


For hooks done right I recommend people look into SolidJs https://www.solidjs.com/.

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.


I really puzzled why no one switched to hooks + suspense. I started to use suspense when it was alpha - you don't need any support for this from react itself: just catch promise in error boundary and display placeholder.

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.


I started doing this when the third party apollo-react-hooks package came out with support for it years ago. It was wonderful. I have stopped based on hazy recommendations from the react team about what the future looks like, but I miss it.

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.


There’s a Suspense component that does that catch-and-placeholder functionality much more cleanly now.

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.


Because react no longer renders child components in the same stack as the parent after concurrent mode, I think?


The current enthusiasm around React seems somewhat reminiscent of jQuery about 15 years back. It was the first library to solve a problem that plagued the entire industry. It was widely adopted and considered almost a "standard", to the point where you will now find an entire graveyard of Stackoverflow questions about DOM manipulation where the answer is "just use jQuery".

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.


I wrote jQuery in production for 5 years and then switched to React for 6 years in production at a half dozen companies. In my opinion, nothing can be improved further. I love writing React, it solves every problem you can think of and scales for large codebases.

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.


I agree, jQuery has been quite the workhorse, so much so that Angular 1 had a built in subset of jQuery called jqLite


What I took away from the article is actually how similar the methods are.

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.


Svelte makes React hooks feel like boilerplate.


The main takeaway from this article is how to improve the marketing of your own content. This is a fluff piece that is mostly copypasta from React documentation. How did it frontpage? The title, and maybe some klout from the domain name. Clickbait gets you pretty far.

Either way, I hope you’ll join me in flagging it.


I haven't done a great deal of React, but having inherited a codebase with both class components and hooks sprinkled through, the class components are better organized if the component actually does anything useful.

Hooks seem to be fine for dumb little UI bits that you'd use directives for in AngularJS.


interestingly, this concept has seeped into mobile app development.

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


I could wrong but I remember that these peoples copied how you can decoratively write UI aka JSX. Did they also copy hooks ? Not that it is a bad thing to copy, just curious hope it looks.


https://www.christopherbiscardi.com/swift-ui-state-property-...

Generally, it seems like it. But I'm definitely not taking an authoritative stand here!


A bit off topic: do React jobs pay better in your area?

Here in Bulgaria these jobs pay about 30% more than identical Vue positions. It's very unfortunate for people like me


Very superficial, but IME React developers tend to be more full stack, more likely to use TypeScript, be aware of tooling, etc., which I think suggests a higher level of “competency” (although not necessarily quality or complexity of code produced).


Maybe don’t identify as an X developer, and get the better paying job?


But then I'll have to use React


Ya React devs produce 30% more code than Vue devs. I’m just joking, but it very unfortunate for companies to pay more for a given flavour of UI library.


Hooks are a great start, I hope people embrance the functional paradigm more and we don't have another hype cycle of OOP for some reason ..


See - I'm sort of on the opposite side of this coin.

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.


You can and should still write mostly pure functional components. The difference for me is that I don't need a container component to manage state and side effects now. I can abstract those behaviors into hooks and test them in isolation.


I don't get that, of course it's not functional code in the same way it could be with Haskell or some other functional language, but the main benefits and the way of thinking can be done with JS, and its a much better part of JS than the prototypal inheritance with class sugar syntax part.

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


> The main point of fuctional paradigm in engineering is composition over inheritance

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.


worse, it's actually much more difficult to test states in 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?


You test the hook itself. It is a function after all and using hook testing library, it is fairly easy to test.

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.


FP and OOP both bring their own tradeoffs, none is superior to the other. The trick is to use both.


And suspense is the worst thing to happen to React


Why is that?

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.


it optimizes for not thinking about loading states, and the way it's normally used (hook fetches data, suspends) results in you having to go out of your way to add a loading state near the component that suspended, as you have to create an intermediate component and a loading component, splitting what should be a single concern into several, and it results in a lot of duplication that previously could be addressed by just having an `isLoading` prop

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


>> add a loading state near the component that suspended, as you have to create an intermediate component and a loading component, splitting what should be a single concern into several

I'm not following. Can you just wrap the component that suspends with <Suspense fallback={SharedLoadingUI} /> ? The SharedLoadingUI could help with duplication right ?


Loading Boundary type things do have that issue, but the part where you can delay any update for the 70ms your very fast API response takes, while showing an inline loading indicator, is a real UX win.


If you go by hacker news crowd, everyone despises React and SPAs are worst thing ever happened to web. In reality it exactly opposite. Most people use React, are satisfied and SPAs perform way better than your usual website that refreshes on every click.


SPAs are fine. Server-side rendering is fine, too. What's not fine is using SPAs for static pages or server-side rendering for highly interactive web applications.

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.


Refreshing on every click is super fast if you're not loading megabytes of JS.

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. Brilliant new idea. It was a blast working with them in MediaWiki in 2005. JS community is adorable. They are like a little infant rushing to you with a snow on his hands "look pa, it's wet!". I'm excited to see what they are going to rediscover next.


Are you talking about event hooks, like https://m.mediawiki.org/wiki/Manual:Hooks ? If so, your comment is one for the record books of misunderstanding.


How so?

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


You have a common misconception of hooks that they are only lifecycle callbacks, I suggest https://epicreact.dev/myths-about-useeffect/ to get a stronger mental model. They can mimic lifecycle callbacks but they're a superset of that ability. Also, I'm not sure why you chose Mediawiki as the thing that has lifecycle callbacks, the concept has probably been around as long as callbacks have been a thing.




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

Search: