Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Not sure where I stand on this. On one hand function components and hooks give a nice way to extract logic into small reusable bits, way more than with class components.

But on the other hand it becomes clear that decent performance with this approach requires all kinds of tricks and each react version adds more and more of these tricks with a steep learning curve. Shame since react started out with a very small, easy to understand API and that is what made it so popular.



You know, i'm not even sure that hooks were worth all of the problems with maintainability and readability that they've caused.

I don't have the slightest doubts about me being in the minority here, but i've seen a large amount of projects which attempt to use multiple state hooks per component, and as a consequence the dependency arrays become brittle as the code is changed and sooner or later you're stuck with your page being in an endless rendering loop, because your logic isn't clever enough to actually untangle all of the state to decide when it needs to change anything:

  useEffect(() => {
   ... // a whole bunch of code
  }, [foo, bar]);
  
  useEffect(() => {
   ... // a whole bunch of code
  }, [bar, baz]);
  
  useEffect(() => {
   ... // a whole bunch of code
  }, [foo, bar, baz]);
Sure, you can be responsible with that stuff when you're the one writing the code, but when you open up someone else's project and have "the business" breathing down on your neck but have to untangle their uncommented code, you feel like some of the features are footguns in disguise. At least with shouldComponentUpdate all of the code that decides whether something should be done was in one place.

You could easily comment that code out or change it. But with hooks, you need to untangle how all of the related calls to setState work and where every method for every state hook is called and why. I'm not sure that it was a good idea to decouple those.


I’m with you.

They’re confusing. The magic is too high. They maintain state in an invisible way. The abstractions are hard to follow, especially when you roll your own.

The key issue for me is that they hide the rendering lifecycle from you. Everything’s implied. One of the complaints with classes was that you had to learn the arcane weirdness of the lifecycle. But a) it’s not that weird and b) it’s much easier to learn when the callbacks are named after the stage in the lifecycle. The hooks way makes all that invisible.

That said, I’m quite fond of the ‘[value, setter]’ return type from useState. It’s nice to use and nice to write. Just don’t ask me to explain how it works for multiple re renders.


Yeah, and sometimes it feels like I'm writing a really cumbersome dialect of javascript.

I mean, something simple like:

  let i = 0;
  const inc = () => i+=1;
becomes:

  const [i, setI] = useState(0);
  const inc = useCallback(() => setI(currentI => currentI + 1));
Every single thing needs layers of abstractions upon layers of abstractions.

For all the hate angularJS got, at least with dirty checking your javascript was just plain javascript. I'm leaning more and more towards svelte, which offers this, but more performant than with dirty checking.


> const inc = useCallback(() => setI(currentI => currentI + 1));

This might actually even have a bug and would need to be even longer to properly function :-D

I think you need to add [i] as a dependency, no?


No. I'm using a callback in the setter which gives me the latest i and the setter never changes. If instead I would use setI(i+1) I'd have to add i as a dependency, but since state updates happen asynchronous, this would have a bug when calling it multiple times before the state is flushed.


The useCallback is redundant, the rest is still valid but I'd rather have it this way than magical variables where a reassignment makes the framework do work. This way I can just find all references of "setI"


I find hooks are best when used sparingly. I once worked on a project that went all in on hooks and some bugs were very hard to track down. Stale closures can be a headache too. I've been meaning to capture the gotchas I've seen into a website. Maybe I will one day. All in all, hooks get the job done but I'm not really a fan.


It's tricky. I've had some nasty stale closure bugs too. The ESLint hook plugin fixes these, but I wish that plugin wasn't required. And some of the hook rules are puzzling at first.

But, I've had way more annoying problems using class-based syntax. Twice the code, four times the pain. And TS type inference doesn't work as nicely with classes.

Vue got it right in many ways. At least back in the 2.0 days, the last I've worked with it.


For a good while I thought I was the only one who is against hooks. It's good to see there are two of us. By the looks of the other comments here, maybe even three.


There are a number of people that dislike hooks, many of who are vocal in React threads. Basically the entire classes-are-fine camp do, and there are a few of us in the "the React team doesn't appear to understand the functional paradigm" camp.

I fully expect that in a few years React will be widely seen as a J2EE-like poster child of over-engineering shark jumping for the functional paradigm. I already do.


Hooks never grew on me.

I pretty much stopped paying attention to new versions after React 14.

KISS - keep it stupid simple.

I suspect that some Devs new to JS didn't like the various definitions of "this", and Hooks was supposed to help them out.


> I suspect that some Devs new to JS didn't like the various definitions of "this", and Hooks was supposed to help them out.

No, stale closures caused by hooks are more confusing than what "this" has ever been, especially since this-related problems are mitigated with fat arrows and class property assignment. No, as far as I understand, the driving force behind the hooks was the vision of the coming concurrent mode, for which class components posed some difficult problems somehow.


Hooks are a response to a very specific need. They do wonders in terms of what you are able to accomplish by allowing you to encapsulate side-effects and state management. If you are writing complex or large applications, the ROI is clear.

Now, there are obviously bad ways to use (no pun intended) any tool. Doing things right requires some planning, and learning some patterns. Many effects that also potentially relate to each other should be encapsulated into custom hooks, for example. And it's often a mistake to just make everything hook-dependant—think about how would a non-react bit of the page interact with this feature, or how can you unit-test without dealing with state and component lifecycles.

Learning how to do this in a way that is approachable, maintainable and that closely follows the domain model is not necessarily easy, but that is true for almost every aspect of software engineering, including how to organize functions or classes.

The same is true for things like Suspense. Again, if you have worked on a large application, you'll see that managing side-effects, data that comes (partially or fully) from a backend, and providing a clean and performant experience (no cascading, reasonable errors) is hard. Suspense answers to a real need. Whether you like the solution they propose or not, that there's throwing of Promises involved is, honestly, an implementation detail—and I'd say it works great.

And if these features are not for you, don't use them. And if React is not for you, don't use it. If you have to work on a codebase that is not well architected, full of tangled useEffects, you'd probably have a similarly bad experience if the application was class-component-based or built using a different framework.


People can write bad code in any framework. If it's that difficult to uncoil declarative state in your head the component is poorly written, hooks or not.


You can still use class components where you feel the need.


I’ve found a lot of the elegance of hooks disappears when you start maintaining the dependency lists. It’s far to easy to introduce subtle bugs or huge performance regressions with innocent looking changes.

Feels like we need a new language with lazy evaluation and a smart compiler that handles this for you for this paradigm to really shine.


What are you talking about? How did using classes instead of functions improve performance?


Certain performance improvements and mitigations for common performance problems in something like React require bi-directional interaction of state and state-related events with instances of components. Classes/objects are a pretty natural fit for that, obviously. To do the same with functions, you have to... re-create parts of a class or object system, which is what they did with Hooks.

That would be the "all kinds of tricks" the OP referred to. Not that going from one to the other is inherently a win, but that to match the capabilities and ergonomics that classes & objects come with out of the box for addressing state-related performance improvements & state-related features, with "functional" React, you have to resort to "all kinds of tricks", i.e. write your own version of some of the OO things you need to get the job done semi-cleanly (which is exactly what the React team did with Hooks).

The result, to put it in only slightly unfair terms, is that React has enabled their community to use a really bad and half-baked OO system with bizarre syntax & behavior, all so that no-one has to use the "class" keyword sometimes. They didn't do that for funzies, but because some of the things they wanted to do, including things related to performance, required OO-like features to be workable, but for whatever reason they didn't want to rely on built-in language features for that, nor tell their community that sometimes they would have to use those built-in language features. Instead they opted for... Hooks.


In general a function component does a lot more work than a class render function. Some of it is just about handling cases that we probably should have done with classes as well, but often didn't. The result is that everything must be carefully memoized (or not, it's sometimes hard to predict). For example, should you memoize a redux selector function like useSelector(state => state.x)? Should you wrap every function in useCallback? With classes we had arrow methods that ran once, while useCallback will be run every time the function component is run. We have to continuously check that function references are stable, if it matters, and if we care enough to bloat the function with various memo calls.

Now I'm not at all saying that class components are better. Hooks are mostly easier to work with. But there's no doubt that function components has me using too much brain power and lines of code on memoization. I like the new features the react team are working on, but I hope they'll eventually do something about the stable function reference problem as well.




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

Search: