Hacker News new | past | comments | ask | show | jobs | submit login
Were React hooks a mistake? (jakelazaroff.com)
96 points by mpweiher on March 10, 2023 | hide | past | favorite | 143 comments



Signals and hooks are solving different problems.

React was designed to allow you to think functionally about UI. Your UI should be a pure function of your state. This has always been the goal. All your code runs on every render unless you specifically tell it not to.

Signals are the opposite. A solidJS component runs once, and only the reactive bits ever update. You have to opt into reactivity instead of opting out of it.

The post gives an example of an unintuitive use of hooks, but misses the point. Yes, it's a tricky use case, but the ability to write custom hooks means you need to solve this problem once per codebase at most. The author's "kludgy" solution just needs to be put in a `useTimeout` hook (https://usehooks-ts.com/react-hook/use-timeout). The kludge shouldn't live in the component.

There are some weird cases when you rerender the whole world whenever state changes, but there are plenty of weird cases that it prevents. React's model encourages treating variables immutably and preferring pure functions for modeling your domain. This is hard, because it's not how most JS devs learn to write code before coming to React. But knowing that the whole world is going to rerender gives you tremendous certainty in other areas. Depending on how complex and reactive your UI is, maybe signals would work for you, but if most of what you see on the page is reactive, I'd probably prefer the React model.


> Your UI should be a pure function of your state.

Except this has always been BS, and IMHO this fundamental inconsistency at the heart of the model is why the wheel has to keep on turning.

https://blog.metaobject.com/2018/12/uis-are-not-pure-functio...

(Read to the end...)

https://github.com/reactjs/react-basic/pull/12


> "Except for games, UIs are actually very stable, more stable than the model. You have chrome, viewers, tools etc. What is a (somewhat) pure mapping from the model is the data that is displayed in the UI, but not the entire UI.

So if we don't make the incorrect assumption that UIs are unstable (pure functions of model), then we don't have to expend additional and fragile effort to re-create that necessary stability."

I think this could be the key takeaway from the linked article. It hits the nail on the head, on the other hand though, pure functions are _really_ nice, so I believe in the future development of UI frameworks will congregate in that direction, but in a more fine-grained way than React et al.


> on the other hand though, pure functions are _really_ nice

Yes, and therefore it would be really nice if they were an appropriate model here. But they're not. It would also be really convenient if π were 3, but it's not, and pretending that it is 3 just doesn't give you good circles.

https://en.wikipedia.org/wiki/Streetlight_effect


> Your UI should be a pure function of your state.

I think you are mixing metaphors here.

An ideal React component was supposed to be a pure function that takes in props and spits out a piece of UI. This ideal has never been reached, or even within reach, because once you introduce component state, you lose the purity of your function — its output no longer depends solely on the input (which is what defines a pure function), but can vary depending on the internal state.

When, however, you say that "your UI should be a pure function of your state" (and when we bracket out the word 'pure' that we addressed in the previous paragraph), then signals are just as good a a way to achieve this as hooks are, or React class components were. You modify your state; some code runs; your UI gets updated to reflect the latest state of your state; voila, the UI becomes a function of the state.


> Your UI should be a pure function of your state.

I have seen this expressed elsewhere and and how schwartzworld is using it seems to be the common usage and makes sense to me.

So if my UI, the DOM on a page, should be a pure function of your state then that means when I input the same state to my function I should always get the same DOM on the page.

So if my complete state can be represented by { x: 10, s: 'hi there' } and the resulting DOM can be expressed with nonsensical <div>10 hi there</div> it does not matter what gyrations/changes the state goes through as soon(baring rendering delays or optimizations) as it is { x: 10, s: 'hi there' } then the DOM can be expressed with <div>10 hi there</div>.

> your UI gets updated to reflect the latest state of your state; voila, the UI becomes a function of the state.

I read what you wrote here as expressing the same idea.


With this interpretation though, the statement about the UI being a (pure) function of the state loses any concrete meaning. Because what you are in fact saying is that you just want your UI to be in sync with your state. Which any framework will give you, because being out of sync with your state means that your app is broken; and it is the purpose of any framework in existence to help you prevent your app from breaking.


> Which any framework will give you, because being out of sync with your state means that your app is broken; and it is the purpose of any framework in existence to help you prevent your app from breaking.

That is common now yes and I think that is a good think. Uncommon techniques, now, like storing data in the DOM, uncareful jquery usage, and I am sure other techniques can deny/break a pure functional relationship.

The above is mostly historically why it is important and was not universal. How it ties into what schwartzworld said is that in react you ensure that the UI is a (pure) function of the state by default, no additional work. In signals you have to make sure to opt in every piece of state into reactivity. So I read schwartzworld's argument is that it is easier to keep(?) or at least produce the positive property of keeping the UI a pure function of the state with react than it is with signals because you do not have to do the work of opting in each piece of the state to reactivity.


I understand the purity constraint as the need for idempotency of the rendering function. Is that right?


One of the promises of using hooks was that you wouldn't have to care about things being recreated every render. Gone were the days of complex update logic in lifecycle methods like `componentWillUpdate()`, `componentWillReceiveProps()`, `componentDidReceiveProps()` etc. Now you use clean, functional components everywhere, even if they needed local state. No kludge, just clean code.

But most of the production functional React code I see is sprinkled so liberally with complex `useCallback()`, `useMemo()` and `useRef()` calls to prevent unnecessary updates that it seems just as bad as before. Worse, sometimes, because the hooks pattern can encourage more indirection. I still find myself thinking all the time about object references in state, cloning vs. mutating, dependency arrays with 5, 6, 7 distinct dependencies.

Honestly, I don't know if it's any better than it used to be. At least the `componentDidReceiveProps()` logic tended to all be in one place.


Most of the usecallback or usememo you should try to remove. They are usually there to fix some other rendering issues (mostly performance).

But I also don’t understand, why react renders all child components if the parent renders. As long as their props stay the same, they should just stay as they are.


> But I also don’t understand, why react renders all child components if the parent renders. As long as their props stay the same, they should just stay as they are.

It "renders" all child components if their parent renders in order to check if their props have changed or not.

If the component has not actually changed, it does not actually re-draw it.


I agree with this take! And class-scoped callbacks kept the render function so clean, I feel like hooks really muddled up the API with regards to render. It also created far-more proprietary-to-React logic.


All of this tricky reasoning about what executed when - and what is closed over vs not - is challenging for most developers. The class version is the most straight forward by a wide margin compared to either React function components or Solid functions. I am happy writing hooks and figuring out the right way to express my effect conditions, but I read a lot of code that gets it wrong, and when I talk to devs they often don’t have as strong a mental model for lexical scope, lifetimes, and closures. Ultimately I think either function API is too subtle for the median developer I’ve worked with.


The class version is the most straight forward by a wide margin compared to either React function components or Solid functions.

Dan Abramov wrote a great explanation a few years ago about an important difference between classes and functions for components, and a particular feature of classes that makes it harder to work with them at scale - prop capturing. https://overreacted.io/how-are-function-components-different... It's worth reading if you think classes are more straightforward.


Dan Abramov destroyed modern React when he turned his back on Redux.


Redux has very little to do with React. It's not bound to React, you can use it with any framework, or without a framework. You can't 'destroy React' by stopping dev on a library that's not a part of it.

Plus redux is still maintained. You can still use it if you want to. In my opinion there are better options that plain Redux available now (some based on Redux eg RTK or Redux-Query, and some not eg Jotai, tRPC, react-query). RTK is even recommended by the Redux team.

The fact Dan chose to work on other things is great. He's a talented dev and it's good that other projects benefit from his knowledge.


Hi, I'm the current primary Redux maintainer, and have been since 2016, when Dan handed the maintainer keys to myself and Tim Dorr.

I'm actually genuinely _not_ sure what you mean here.

For clarity, the timeline was:

- Summer 2015: Dan and Andrew created Redux

- November 2015: Dan got hired at FB to work on React

- Jan-Mar 2016: I got involved writing some Redux docs, and Dan gave me commit rights

- Summer 2016: Dan, who was now busy working on React, gave me and Tim full ownership of the project.

He's had some advisory thoughts and suggestions since then (most notably a couple tweaks to our proposed React-Redux hooks API in early 2019), but has otherwise _not_ been involved with Redux's development since 2016.

Honestly, this has been a _good_ thing. Based on my conversations with Dan, he most likely would have mostly left Redux's design and API as it was in 2016-ish: very minimal, and requiring _lots_ of handwritten boilerplate code to do anything useful.

Instead, I came up with the idea for our Redux Toolkit package that wraps the Redux core and provides a better API for writing standard Redux logic, worked with others to build that, wrote the documentation, shipped it, wrote new tutorials using RTK as the default, designed and shipped React-Redux hooks, and did the work to encourage folks to use the newer Redux APIs. For that matter, React-Redux version 5, which had some major perf improvements, only happened because a user offered to PR the rewrite they'd done for themselves, and I worked to oversee that effort and make sure it covered all the edge cases.

I honestly don't think Dan would have done any of that, both because he would have been splitting his efforts between working on React as his day job and Redux on the side, and also because I don't think he would have felt the need to push Redux forward and improve its usage patterns.

So, the actual history _was_ for the best. Dan got to focus on React, and I was able to pick up Redux and modernize it.

Some further reading for background and history:

- https://blog.isquaredsoftware.com/2019/10/redux-toolkit-1.0/

- https://blog.isquaredsoftware.com/2022/06/presentations-mode...

- https://blog.isquaredsoftware.com/2018/11/react-redux-histor...

- https://redux.js.org/introduction/why-rtk-is-redux-today


Yeah I absolutely love hooks, but it definitely has a learning curve. But damn, you can do nearly anything with it. One of my favorite projects involved using hooks to manage p2p connections using WebRTC, which would have been a nightmare to write without hooks. Haven't looked into signals yet, it's interesting that it almost looks identical. I wonder how it decides when to rerender.


So you're building business logic into your view components? This used to be simple in the old days when we used MVC.


No it wasn't, MVC was a mess because your views would trigger your controllers, which would propagate changes back down to your views in entirely unpredictable and inconsistent ways.

With hooks you're still free to manage business logic elsewhere, it just means that you can trivially reuse them across your application without worrying about order-dependence. So I was able to write a hook that returned connection state, and then drove my views off of that reactively.


I think you're thinking of MVVM, not MVC.


It's MVC. Having your views driven off models is nice in theory, but a disaster in practice as the number of models proliferates. You lose control over how and when your views render, which results in all sorts of subtle and strange bugs that are difficult to debug. Each view may have its own logic for what to do when the model changes in specific ways, and what controller events to fire in response. Without unidirectional data flow, trying to even reproduce a bug becomes a nightmare.


MVC is unidirectional.


Nope, not between views. View triggers controller, updates model, triggers some random set of views to update, which triggers controllers, which triggers other models, etc. There is no hierarchy there, nor any batching to avoid rendering inconsistent intermediate state. You have no control of how an update affects your UI.

With React, data flows down from the top of the component hierarchy, always. And it doesn't render until all state changes have been made, so you only render after your data has reached consistency.


Incorrect. In fact, you can use React as the 'V' in MVC. That's how it was orignally designed. See here: https://github.com/facebook/react/tree/015833e5942ce55cf31ae...

I am done with this thread.


One of my complaints about reading computer science and/or programming books in the early 2000(or late 1990s?) was the contradictory definitions of MVC in books I read at the time. I think one book even indicated that MVVM(I think) was often referred to as MVC as an explanation for why this happened. So it understandable people still have conflicting definitions.

Here is a random example[1] I pulled up of someone one thinking MVC is not unidirectional but MVVM is. I am not commenting on the correctness of this impression.

> MVVM is better than MVC/MVP because of its unidirectional data and dependency flow. Dependency is one way,...

[1] https://dev.to/vtsen/mvc-vs-mvp-vs-mvvm-design-patterns-443n....


That's not what we're talking about at all, and you've lost the plot. The reason why React has won out for over a decade is because of the abject failure of MVC to materialize its benefits. It just doesn't scale.


What about it doesn't scale? Most UI frameworks, including ASP.NET Core, JSP and JSF (Java based frameworks), Ruby on Rails, and Django (Python) are all based on MVC. If MVC doesn't scale the folks who maintain those frameworks would have noticed by now.


Which honestly was most SaaS products. My first internship was MVVM and killed my motivation to ever do web development.

9 years later my new work involves some react (barely 15% of the job tbh) and I really don't hate it as much.


The issue is that React was never (as far as I'm aware, please correct me if I'm wrong) encorporated into a full MVC (or similar) framework; its original intent was to be just the V, taking state (the model) and rendering it.

But what happened instead is that people wrote the controller parts into React components as well. In some codebases there's a very clean separation - 'logic' components that handle and manipulate state, and simpler 'view' components that just take props and render something.

But I haven't yet seen a framework that uses React as just a view layer.


I think it's more than the median developer, and I think part of it may be due to React being a victim of its own success in some ways. The scope of requirements for many kinds of common hooks has expanded dramatically. Yesterday I found a problem in a hook that is part of a library downloaded 1M+ times a week.


IMO it's more like anyone who doesn't spend over 50% of their time thinking about React re-renders whenever they're in a React component. The level of effort to properly use hooks is insane, so them being touted as a "less boilerplate!" way of writing "more functional" (literally the opposite in practice) code is egregious.


I meant for this to be included in the scope of my comment but I was short on time. You stated it better, and you’re completely right. The problem I was alluding to was related to a hook deciding when to rerender based on dependency lists. It was setup such that some people may expect it to rerender in a certain case and others may not, and it wasn’t an uncommon or excessively complicated use-case.

React can’t win here because they have a hard stance on when to value performance (avoiding re-renders) vs. convenience (ease of abstraction usage).


because they don't have a hard stance*

important typo there.


Yep, here is the React Router team getting it wrong(among other things): https://github.com/remix-run/react-router/issues/7634


> All of this tricky reasoning about what executed when

I may be too battle hardened, but here’s a simple rule for React: As soon as you have to reason about execution ordering, you have taken a wrong step and something is wrong.

Your code shouldn’t depend on effect ordering. Trying to do that is like trying to write code that depends on the precise ordering of thread execution in a multi-threaded program. You’re gonna have a bad time.


There might be a subtle rationalization of lack of competence indeed. Which is complicated because the ego can't accept that. People is used (and to be fair it really needs) to market itself plenty competent even when is not (impostor's syndrome conversations come out of this). But this goes way beyond React hooks. A lot of hate of OOP is due to self-incompetency on doing proper libraries doing the right powerful abstractions from the start.

For an erudite writer it would be painful to hear non-erudite writers blame the pen and paper for the underperforming outcome that they expected to see on their writing. And the cost of correcting that issue in them when modesty is so scarce would make the cost so high an even dangerous, that should be no surprise that only their silence gets in return. Mediocrity posing as great work rejoices.


Software really has inadequate training. People get very diverse training experiences and that can be a source of friction between developers at different qualities and competencies.

It also moves so quickly that things don't settle and people have time to get intimate with frameworks and paradigms - I'm talking like 5 year timelines with libraries doing only maintenance updates. Front-end was bad for this because of the browser wars and JS's foundations gave it a really rocky start. The allure of starting over with a new library to get away from the current "bad" way of doing things is often repeated: SPA frameworks over vanilla/jQuery sites, hooks, and now maybe signals?

There are also jobs asking you to be spread very thin among things, while keeping deadlines, so you end up googling around to get it to work, even if it violates some principles of the frameworks you're using. That doesn't really lead to expertise.


Yeah: I remember drawing boxes and arrows in CS61A at UC Berkeley when learning about closures, but not everyone I work with took that class, remembers that stuff, or was taught it in the first place. Very few people can on-the-job Google to learn a mental model that takes undergrads a few weeks of diligent practice.


The best take I've been offered is that the problem with Hooks is that they are kind of a quasi object-oriented solution (more or less it is encapsulating state with a setter/getter) shoe-horned into a quasi-functional render pipeline that demands functional purity at scale (lest your performance falls off a cliff from over-rendering or you end up with odd side effects).

That disconnect is why hooks just don't feel right.


Hooks are often side effects (but not always) and that sounds like "imperative" programming, but actually the side effect is what is inside the useEffect hook, not so much the hook itself.

The component is still a pure function. It "returns" side effects without executing them, just not as the function return value but through a side channel. But that side channel is sort of a clutch to achieve a pure relation from input to output, just that the output is now a return value from the function plus the sideeffects, which may be executed or not.


"Side effect" in my comment means an unintended side effect from an impure functional component (often a case of missing a useEffect, useMemo, or useCallback).


"Side Effect" in functional programming usually means the function is affecting something other than the output. And calling a hooks really doesn't, they just output the description of such an effect on a side channel.


> usually means the function is affecting something other than the output

That's exactly the point. If you leave an errant function call in a functional component, it's going to affect something other than the output of that component.


Isn’t the best of both worlds what you want? I do a lot of FP and OOP, and both worlds are not perfect. You want to mix them in a way you get the best out of both worlds.


Each has a different mental model and as Evan You said, the host language itself is not suited for a "pure" FP approach and you end up with a leaky abstraction.

As the article states, function closures are how classes are actually implemented in JavaScript so rather than mash the two together, it would seem a more cohesive mental model to simply use a class.

If you step back and look at the broader landscape of UI across desktop (Windows, Mac, Linux), devices (iOS, Android), and web: how many libraries and toolkits are FP and how many are OOP in their abstraction when it comes to managing components and object instances?


If you love OOP, then use Angular. It’s OOP all the way! But you will probably regret using it…

There are also a lot of UI frameworks that are completely functional. Take your pick. (ReScript would be the most obvious choice for a react developer)

But for a good reason the hybrid FP/OOP framework React on JS/TS is one of the most popular.


Even Solid and Preact are OOP in nature since JS closures are how classes are represented in JS.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

    Classes are in fact "special functions", and just as you can define function expressions and function declarations, a class can be defined in two ways: a class expression or a class declaration.
The difference is that React functional components are effectively "parroting" FP and half-in (stateless components) and half-out (hooks). Solid, Preact, Vue are all-out.


By that logic F# is also not an functional language, because it gets compiled to CLR classes.

For me those implementation details are not important. But sure, you’re right.


F# is not a functional language.

Even says so in the docs:

https://learn.microsoft.com/en-us/dotnet/fsharp/style-guide/

    F# has full support for programming with objects in .NET, including classes, interfaces, access modifiers, abstract classes, and so on. For more complicated functional code, such as functions that must be context-aware, objects can easily encapsulate contextual information in ways that functions cannot.
https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref...

    Classes represent the fundamental description of .NET object types; the class is the primary type concept that supports object-oriented programming in F#.
https://learn.microsoft.com/en-us/dotnet/fsharp/what-is-fsha...

    F# has full support for objects, which are useful when you need to blend data and functionality.
The underlying CIL representation of a lambda expression is an object; the functional side of .NET (whether C# or F#) is syntactic sugar on top of an OOP runtime.

(C#/.NET dev by day dabbling in F#)


As is often the case in CS, there are no definitions for plenty of concepts. FP is simply “programming with functions”. Most languages are multi-paradigm, and in my book any language that has lambdas is already functional - but it is a spectrum. Java has ways to express functional programs, but it is of course much less functional than, Haskell - which itself is also not 100% pure FP.


If every language can be functional, than no languages are functional.

Would you agree that C# is not functional?

If you agree that C# is not functional, than F# is also not functional. Nearly every construct that can be expressed in F# can be expressed in C#.


No, C# has plenty of features making functional programming possible, so it is definitely on the spectrum of FP languages.

Let’s go at it from the other direction - is Haskell functional? Because it has unsafe pointer access and unsafe IO, so it can pretty much express every C# program as well, right?


I don’t see how your quotes imply that F# is not a functional language.


It literally says on the box that it's not a functional language and it's clear that the actual underlying CIL is class based. If you still want to call that FP, then by all means. But then so is C# since you can do nearly exactly the same things in C#.


Can you quote this part?


You can find arguments to justify any point of view :)


> function closures are how classes are actually implemented in JavaScript

Quite the opposite with V8.


I go back and forth on this - overall, I don't think hooks were a technical mistake (it's a fairly clean way to share code that is not cleanly shared in class components) but... I they accidentally knock a LOT of developers out of the pit of success. Overall - I think they accidentally made React apps a lot more unwieldy.

My sweet spot for React was when it was class based components mixed with purely functional components.

That combo led to all sorts of "happy accidents" in developer productivity. Logic that needed state tended to be clumped into a relatively small number of class based components, developers actively wanted to avoid adding state to new components because they had to write a lot more code to do it.

So you end up with a structure that looks like several small trees in the code: A single class based component managing the state for several pure function components under it. Overall - this was a fairly happy spot to be. State is contained, performance is usually pretty predictable, pure mappings are encouraged.

Hooks... well - they made state too easy again. Now adding state to a new component isn't a 20 line change (conversion to class). It's just dropping a new "useState()/useEffect()" call in near the top. Very easy. So easy you no longer really think about why that might not be such a great idea.

Now you end up with state creeping through the entire component tree. Renders are hard to predict because any parent component might re-render at any point, you're neck deep into useMemo and useCallback to try to prevent all those extra renders from really killing performance. This is not such a happy place to be.

Can those downsides be avoided? Of course. But developers no longer fall into the right path by default. You have to wrangle them there, and that's a huge downside.


I think I am getting old and cynical. These days whenever I read a blog claiming some approach has recently "exploded in popularity" and implying it's the 'ultimate solution' to whatever problem space, I mentally start counting the days until its inevitable replacement with the next fad...


Programming trends move 100x faster in webdev than in other areas for some reason.


That definitely used to be the case, but it seems that webdev has matured to the point where you don't need to learn a new framework every 6 months. React turns 10(!) this year, and React hooks just turned 4.


I think that’s an entirely reasonable response - thanks for suppressing my inner cynic!


Am I the only one who thought that class-based React components made perfect logical sense and "hooks" are way too magical and confusing? Plus I ran into this exact drawback (trying to access state from within callbacks) on a recent project. I feel like I'm taking crazy pills when I see hooks everywhere.


Class based components are easy to pick up, but they don't scale. They're really hard to refactor, and make it hard to reuse behavior. If you have a simple app though, they'll do fine.

Often with hooks, you'll have to think differently in order to figure out how to accomplish what you want to do more functionally. That up-front cost ends up paying back later because you'll rarely need to touch it, and it's dead-simple to reuse somewhere else. This pattern from the blog post of "fetching state from within a setTimeout" is rare, but your example of accessing state from within a callback should be manageable.

Here's the thing: even though the example from the blog post is strange, the advantage of hooks is that you can pull that pattern out into a hook! So instead of `useState`, you'd call your hook that returns the reactive value, a setter, and a getter that uses the ref under the hood. Then you can use that getter in the `setTimeout`, and you never have to think about this problem again when you have to do this for any other piece of state!


“They don’t scale”

They scale great. Decorators are incredibly underrated for code reuse.


You can't compose behaviors effectively with class based components, and the fact that you only have one of each lifecycle method means that they become an absolute mess as you scale with complexity. Each behavior gets split up across multiple lifecycle methods, mixed in with every other behavior. With hooks, you can encapsulate each behavior into a single hook function that handles its complete lifecycle, and then trivially use it anywhere else.


I still exclusively use class components to this day. They are incredibly easy to reason about and solve every problem I’ve had.

Hooks from the start seemed like a functional mess, everytime I see it I also feel like I’m taking crazy pills.


I miss using React Redux.

I'm a back-end dev primarily and never had a good time writing front-end code. Redux just clicked for me. The model made perfect sense! Events from components get handled, and the state is changed. The state is then re-rendered. Beautiful.

It feels like hooks replaced a lot of what redux was doing, yet I don't have the same sense of reasoning as I did with redux.

But again, I'm mostly a back-end dev.


I use Redux with hooks. It's fine and I prefer the hooks syntax to the `connect` stuff.


Modern redux is okay, the first redux was just an abomination of boilerplate code, that gave frontend developers the feeling they are doing „clean architecture“ now too…


I mostly have to work without RTK, and I still prefer that old Redux over anything else. The boilerplate is mostly just thinking clearly about state changes, and if you avoid that too much (in any framework) you get into trouble.


Whenever I see less experienced developers writing Redux, the result is just an abomination of chaos. I prefer naive spaghetti code over redux, because I can easily rewrite that into good code. But if the logic for one operation is distributed across dozens of files, it gets really hard to refactor.

And mediocre developers are the reality, i don’t think there are many teams, where everyone writes awesome code all the time.


I can't agree. I've never worked with people that don't understand a convention like "put actions in actions.ts and selectors in selectors.ts". Nor is that really a big deal because Redux is very easy to refactor, it's just pure functions.


Sure, they do that. But what do I do with a 5000 lines of actions and selectors that are all used somewhere, are all named somehow similar/unclear and only work in a specific combination/order, nobody remembers anymore?


Actually with redux it is trivial to record, replay, even filter actions.

That's one reason I like redux so much. I expect this functionality from any other state management solutions I encounter. Some others indeed do have this.


Do your job? I mean if it is that complicated, it has to have some purpose and value behind it, also you wouldn't be hired or tasked to improve it. With redux there is a good chance the actions and state are at least very transparent, for example when using the devtools.

In my experience it's a lot harder to deal with mutable state in some context provider or something.


@reduxjs/toolkit is absolved most, if not all, of the boilerplate. It's been a joy to use.


Yeah, it got much better now.

But since react query and swr I think most applications don’t need state management anymore.

If you have complex state, it makes sense. But just to fetch/refetch some queries redux is overkill.

Most redux (and also angular ngrx/ngxs) projects I’ve seen, are just over engineered. They solve an easy task in a very complicated way.


I find using query/rtk/etc to be more trouble than they're worth tbh. It's more black-boxing then I'm comfortable with, personally. I want my side effects explicit. I've found a lot of success with redux/redux-observable which is super clean, at least for me.


Okay, for me the functionality of those libraries is quite straight forward. You can just turn on/off whatever you need with one setting.

But usually the defaults is what you want. Your user is on a bad connection? Automatic retries are what you get and what the user needs. You user just got back online? React query notices that event and refetches the failed queries.

You need all those features turned off? Just turn them off in the config object.


The other day I created a backend using Redux and RTK Query...


Oh gawd. Redux needs to be burned with fire.


I have not seen a better client state management than re-frame. https://github.com/day8/re-frame

Notably the author of re-frame has been weary of hooks.

I think that’s for a good reason. The approach in re frame feels like the best way to manage state so far for a react based app. Everything that changes state flows through an event. State can only be observed through subscriptions. Side effects are isolated to their own type of event. Debugging and testing are so straight forward with these concepts.

Redux got close but it has two problems in my mind. Like hooks, it encapsulates for no good reason. Put the state in one thing that you can observe holistically before and after pure events. It also has too much boiler plate.


Slightly off-topic but I'm struggling to understand the choice of that weird looping font the web page uses for keywords in code.


I didn't know what you were referring to since I use FF with NoScript. Enabled javascript for the domain and whoa, that is really hard to read. Very strange choice, the article is much easier to read with the default browser fonts. So I guess javascript-blockers FTW?


Yes, and it’s not just because it’s cursive. I’m in my late sixties so am thoroughly familiar with cursive — just have no use for it as a font, much less in code.


Cursive?


Yes, that's the one. Very hard to read quickly, and an odd choice for a use case that I'd think would require absolute clarity and rapid recognition.


If you know cursive, it usually reads fast and clear. I don't think it is widely taught anymore though, as it was mostly a footnote when I was in school many years ago. It is a strange choice for the article considering the rarity of cursive these days.


The cursive throws me off and I am heavily distracted by it. teho (to each his own)


I'd argue, on the contrary, it's perfect for keywords: you recognized those mostly by shape anyway, so now they don't even look like actual names which is an upside for me.


it's not cursive because the letters are not connected (unless my fontconfig setup is messed up). these are cursive letterforms completely divorced from their utility which makes them awkward and jarring


That's called Cursive. A an elegant style of handwriting from a more civilized age.


If you're handwriting, sure. But only for your code snippets? That's a bit weird.


Not uncommon but still baffling


Try Operator Mono Lig -- beautiful & readable italics for code styling.


It is awful. Fortunately most people seem to agree and do not use it.


Yeah that's how Cascadia Code renders italic.


It’s cursive. They used to teach it in school.


It's funny, it's just font-style: italic

I've never seen that before.


IMHO best-case scenario with React is there's a lot of "you're holding it wrong" in which case we have to ask ourselves, is it that most programmers aren't skilled enough or that the tool is designed wrong? Both are possible but the latter is in general more likely.

I also think there's an issue where react was made to solve Facebook's problems and we're not Facebook.


A bit off-topic: How is any of the code samples intuitive for a beginner to use. I understand and love the reactive principle (and it immense benefit when it comes to sizeable UIs) and high-level react (never coded it) ... but still, the sheer complexity introduced for a simple counter by these 3 evolving snippet.

This is not how I want my grandkids to code software (if the AI overlords let them).


React hooks were a huge mistake because they (along with the huge push by Gaaeron to ditch redux at the same time) disrupted the more declarative styles in React that subjectively relax the mind. Boilerplate code is tedious, but gives the brain a rest from reasoning that many developers actually enjoy. You can really get in the zone wiring up a react/redux app and isolate yourself from re-render logic by focusing on individual stores, reducers, and action logic.

The hooks people came in and said F all that, lets instead create an API that garbles up all the code in one spot, with imperatively nested functions required for more complex use-cases, oh and lets use CONTEXT for state management and just partition items up to deal with re-renders.

They didn't do it on purpose, the react team is highly skilled, but they lacked the wisdom to understand how lower tier or mid-tier developers would use it. I knew plenty of devs that had 3-4 YOE and couldn't grok hooks for years.


Having a good way to compose stateful behaviour in React components is really useful. The alternative is a mess of higher-order components which quickly gets very annoying and tedious. Hooks provide that ability, and they make a lot of cases much easier to write.

Hooks also have some behaviour that makes them harder to understand in certain situations. Closures are quite easy to get wrong, and handling callbacks is quite annoying because they're not stable by default.

I think the timeout example is not ideal, it uses useEffect to handle an event, which is not the way this is supposed to be done and which does lead to various complications. But timeouts are the kind of thing that is much more difficult in hook-based React than it should be. But I don't think this is representative, it's a bit more like a worst case for React for things that should be simple but aren't. You don't write timers all day, you put this in a hook once and write other stuff.


There should be a meme for "useEffect is not an async framework".


Way too much churn in React. People have products to build, they don't want to re-learn a new way to use React every year.


React Hooks came out 4 years ago. React doesn't change dramatically every year and the React devs are careful not to break the old APIs, so you can ignore hooks or other new features if you want.

This complaint comes up in every thread about React and it makes no sense.


Except in the real world opinionated leads and team members forced teams to adopt ONLY hooks going forward, or have to maintain two separate implementations - hook compatible or class component compatible code.

So yes, this is a very sensible concern that transpired with hooks. Simply allowing reverse compatibility doesn't change the fact that hundreds of workplaces forced people to use a less ideal paradigm.


> Except in the real world opinionated leads and team members forced teams to adopt ONLY hooks going forward, or have to maintain two separate implementations - hook compatible or class component compatible code.

That isn't React's fault. Your opinionated team members can enforce all kinds of nonsense. This is a general argument against ever introducing new features in any framework or language, and it's not valid.

> So yes, this is a very sensible concern that transpired with hooks. Simply allowing reverse compatibility doesn't change the fact that hundreds of workplaces forced people to use a less ideal paradigm.

"Less ideal paradigm" is an opinion. There are use cases where hooks make perfect sense and are superior to class components.

And again, what your employer and teammates do doesn't implicate React or the React team! They could have enforced Hooks on all components, and dropped support for the old APIs. What should they have done differently? NOT released an optional feature that is loved by millions of their users, just so none of those users can interrupt your ideal workflow? How entitled can you be?


I pulled a class component into a greenfield project just last week. It worked without a hitch.

You can still use React the way you used it 10 years ago.


I think hooks were a mistake.

I think components should have been made pure functions, with state/effects being provided by higher order components (HOCs).

For example:

  Counter = ({count, setCount}) => {
      // pure function here
  }

  StatefulCounter = withState(Counter, {
      count: {
          setCount: 0
      }
  })


As another child comment said this is how Redux with HoC worked a few years ago. It was very common. Imo working with that paradigm was miserable. You would have a mess of withXXXX type of connections. It was like using decorators or mixins with 0 ergonomics. Hooks are about a million times better.


You're basically describing Redux connected components.


If react, and specifically react using functional components, is going to be a full featured web framework, hooks or something like them are necessary to integrate with the browser and the web of 2023.

The 101 parts of functional programming are a very easy pitch. Stateless behavior, testable functions, no surprising side effects from way down the tree. When it gets to 201 stuff - boxing (like useRef) and dispatch (like useState) complexity creeps back in. That complexity is necessary, however, because that's the stuff people actually want to do.

Hooks let react interact with browser audio and video, mapping tools, and other things that are reasons why people want to use web pages. They're complex, but there are limits to how simple a bridge between a functional programming framework and a mostly stateful host system can be.


Reminds me of a story, The Emperor's New Clothes (H.C. Andersen, 1837).

The emperor gets tricked into buying new extraordinary clothes. They are incredibly fancy, but invisible to anyone "who was unfit for his office, or who was unusually stupid".

The emperor didn't see them, but of course he didn't want to admit that. The whole lot of townspeople didn't see them either, but likewise, no one wanted to admit that.

In the end, the emperor goes to town, naked. Everyone praises his new clothes. Until finally a little boy utters: "But he hasn't got anything on!" and the truth unfolds.

https://andersen.sdu.dk/vaerk/hersholt/TheEmperorsNewClothes...


How would the boy's comment reveal the truth? It should just be rationalized as he wasn't fit for office or unusually stupid. Likewise for the village people - even if they said something it has already been explained away by the initial premise the emperor accepted.


That's an interesting question. The author has long passed away so he won't be able to explain his story.

Perhaps a psychologist could chime in with some probable explanation?


If this was code I'd have to review I'd give the feedback that the wrong information is kept in state.

After start the only thing the setTimeout should do is set the game to be finished and then show the final count to the user. If the code wouldn't have used an alert which definitely is a side effect of a function it gets apparent where the code went outside of the React loop. Not defending that but saying that alert and console.log conflict with React's always re-execute approach and for those cases, as you would have guessed, you can use useEffect and only listen to the finished state to execute your alert.

Throwing a console.log/alert into the direct function body of a component just asks to be spammed by rerenders (assuming the average developer)


React hooks are the best reactive state management for frontend I’ve ever used.

They provide more or less the same functionality as Rxjs, but are 10 times easier to understand.

Yes, they are not perfect, but they solve a very complex problem. It’s the easiest solution I’ve ever seen.


I prefer this, what's it called? observable?

  function createButton() {
    var button = document.createElement("button");
    
    function setClicks(clicks) {
      button.innerText = "This button has been clicked " + clicks + " times";
    }
    
    DB_STATE.get("click", function(err, clicks) {
      if(err) alert(err.message);
      if(clicks == undefined) clicks = "?";
      setClicks(clicks);
    });
    
    DB_STATE.on("click", setClicks);
    
    but.addEventListener("click", function() {
      DB_STATE.do("click");
    });
    
    return button;
  }


Not having heard much about signals my takeaway from this is that they allow you to write functional components assuming the function runs exactly once, thus sidestepping issues with closures etc on updates that cause further runs of the function.

Is that right? If so it's pretty smart I think.

I'm not really a fan of the 'magic' introduced that makes it work via direct DOM manipulation on updates. It seems suspiciously like that's yet another source of mental model confusion that articles will be written about a couple of years from now... but overall it does seem a lot simpler for most usecases.


To me React clicked because it was basically PHP or a template engine, just on the frontend and it rerenders when necessary. You can treat it mostly like that, even if it is more complicated.

Where signals get into trouble (I think) is when the structure of the elements change a lot, and that's when I'd expect bugs in DOM manipulation or state handling to show up. You also need to remember to listen to these signals, otherwise you have something that updates fine under certain circumstances and not others.


During the original hype over hooks, I vividly remember thinking: 'I wonder how long we'll wait before these too are reviled.'

We're getting there.


Only if you weren't writing code for a bigco (imo) - hooks simplified a lot of enormous components to be almost readable


Can you give any example where hooks helped readability without trading it for extremely high complexity behind the scenes? I maintain a large React codebase part-time. The client is a very good friend of mine and I was the one who chose to make my own life miserable by selecting React. Needless to say I won't touch/introduce anything "new and/or popular" for customer projects with a ten-foot pole for the rest of my career.

I used both class-based (thank goodness?) and hooks based components but only coz React ecosystem forced me to, class-based were frozen and left in a sorry state - as if React's dictator-in-chief (forgot whoever they are/were, don't want to know - tried to engage once in writing the "beta" React docs but they were horribly toxic in response for no reason) is the only smart person(s) ever and all these OOP language designers are... No, dear Kim Jong(s), you were the ones abusing JS classes in strange counter productive ways and I easily found better OOP patterns for my class-based components. Then I met hooks - the definition of anti-productive. Not to say people don't have a right to write/reflect whatever madness in their code they want to but for me it's a tragedy their "work" became so popular. I don't mean to belittle people who actually have it but hooks is what comes to mind when I think of what PTSD must feel like. I no longer take up JS/TS SPA projects. For the mental strain - the pay feels peanuts compared to native mobile app development so why bother.


> enormous components

Sounds like an X/Y problem to me.


Well, I don't know about hooks being a mistake.

What I do know is that the font choice in those code blocks was DEFINITELY a mistake.


They were not a mistake, people just need to grok them better. You need to embrace functional purity.


But hooks enable the creation of components that are often the antithesis of functional purity. Side-effects out the wazoo. It's like carbon credits for functional purity rather than the real thing. People just feel better being able to say that they're using a function component.


Hooks encapsulate mutable state. It’s not much different than object-oriented programming with a different syntax.


I copied this code into a current project and the alerts all had the correct counter.


anything that isn't the garbage OOP with its infinite bloat is progress


“Put another way: class components have a single instance per mounted component, but function components have multiple “instances” — one per render. Hooks just further entrench that constraint. It’s the source of all your problems with them”

I see this a lot, it’s probably the misunderstanding that drives most of the dissatisfaction with hooks.

Having a single instance that exists for the whole lifetime of the mounted component was a major advantage of class components, the major drawback being that you kept losing immutability.

The big advantage of functional components is they give you all that immutability, their drawback is you no longer have a single instance that corresponds to the mounted component’s lifetime, because functions are just executed and then they’re gone (until the next time you execute them).

The way to think about hooks is that React will create a internal virtual instance that does correspond to the lifetime of the mounted component, and it will let you hook into that virtual instance from the function (to store state, effects, refs, etc). The idea was that way, you can have both immutability and single-instance-per-lifetime benefits.

Because React is managing all of these internal virtual instances itself, it can do some nice magic like letting you use a clean top-level “useState” function and it Just Works… because behind the scenes it is pairing up each call to that function with each internal virtual instance (using its order in the call stack, which means the magic fails if hooks are called conditionally, hence the rules of hooks).

That’s fundamentally the concept that lets you work with hooks fearlessly. You no longer had to provision each of your class components with their own instance and make sure they were maintaining it properly, instead your function components could just clock in and clock out and React would take care of making sure they were handed the right instance to work with.

If I had to pinpoint the exact mistake React made with Hooks, it was this: saying you could emulate componentDidMount with useEffect(fn, []). It completely obscures this idea of a virtual instance you’re hooking into. They should have been explicit about the virtual instance model and given developers “useDidMount(fn)” and “useWillUnmount(fn)” hooks, explaining that for each function component you write, React is managing an instance for it behind the scenes and React will make sure it runs the provided mount and unmount functions at the appropriate moments in that instance’s lifetime.

One can imagine a hybrid option, a lightweight class definition (so you could set state in constructor, define DidMount, make refs, etc., in the natural way) that would then return a functional component. That would make the long-lived instance explicit, but I think the ergonomics would unfortunately suck: “render() { return function (props) { return <div/> } }” is hideous, for example.


mods, can we ban these React bashing threads which seem to pop up every other day? another recent thread[0]

I get that react is full of problem and many people have to suffer because of it. But we are not even attempting to change status quo here. This thread merely serves as place to rant. and then go back to same problems. At this point, i have most of the opinions memorized and i am not even a React dev.

PS: if people are really into React/vue/angular rants, then have a monthly thread for it.

[0] https://news.ycombinator.com/item?id=35061672


yes, anything that muddles the stack trace is


yes


I would argue 'not exactly'.

Technically, React was the mistake. Eliminating React would prevent React hooks from existing in the first place.


I am so happy I don't have to touch front end code for my career


It was like an admission that their idea couldn’t handle many cases and they made a release valve.

There are other approaches like Signals… or Angular… or our totally different approach:

https://qbix.com/platform/guide/tools#designing

There is really nothing wrong with simply having components without unidirectional flow, with being able to simply pass options down to child components.


Oh... Oh no.


Very enigmatic.




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

Search: