DEV Community

Discussion on: Improving your (Web) Dev Foo

Collapse
 
puritanic profile image
Darkø Tasevski • Edited

This surprised me, I thought hooks (and the "functional component" approach) was supposed to make testing easier compared to conventional class based React components.

We have around 2k tests in one of the projects, Jest/Enzyme combo, and I've noticed that since the introduction of the hooks, you have to jump through a few more hoops if you want the component completely covered. This is particularly true for components with a lot of async/graphql logic.

While it might be possible that I just haven't got used to a new way of testing, I just don't like that sometimes writing the tests for some logic takes more time than it was used to write that logic.

Collapse
 
leob profile image
leob

Right I see, wow 2K tests, that's pretty huge and impressive ... so apart from the testing story, do you think that hooks as such are an improvement over "the old way" ?

Thread Thread
 
puritanic profile image
Darkø Tasevski

I would still use the old class-based way for the components that heavily relly on state management because a component can get crowded with useEffects and useState real quick if there is a lot of logic needed there. Keeping in mind that sometimes we need to take care of the order of the useEffect hooks, it can be really hard to refactor and change code comfortably (but tests can help with that a bit).

For everything else, I would use hooks instead of converting a component to a class, they are definitely an improvement and I'm excited to see what other exotic stuff would React team show us this year :)

Thread Thread
 
leob profile image
leob

Okay, so you say:

"a component can get crowded with useEffects and useState real quick if there is a lot of logic needed there. Keeping in mind that sometimes we need to take care of the order of the useEffect hooks, it can be really hard to refactor and change code comfortably"

It sounds that hooks have a lot of drawbacks then ... however next you say "they are definitely an improvement" - why are they an improvement then?

I've read a number of articles about hooks and while the basic principles aren't that hard to understand a lot of these articles then quickly descend into cases where stuff does get tricky with hooks. It made me wonder then what all the fuss is about, when class based components were in fact pretty easy to understand.

Somehow it seems down to an ideology that FP and 'pure' components are the only true way, and that classes and OO in JS (or at least in React) are something evil, at least that's the impression I get.

Thread Thread
 
puritanic profile image
Darkø Tasevski

Well, that is the only drawback I've noticed, the complexity of the component is increasing exponentially with every new useEffect added. There is a lot of stuff to keep in mind when working with hooks, the order of useEffects is one and you have to use useRef for variables inside the component to prevent rerenders. Kent Dodds has a few short "courses" on egghead with some advanced hook techniques but those are rarely needed. In most cases, it's much better to break down the component into a few other smaller components when you start having trouble with understanding the logic.

They are improvement in a way that we don't have to switch to a class-based component to use a state, we can have lifecycles in functional components and it's really easy to cache data and methods with useMemo and useCallback. IMO, classes are far from evil and they still have their own place in React, but, from what I've heard, transpiling them into pre ES5 code has a lot of overhead code, and there are sure a few other reasons why the functions are better.

I also wholeheartedly agree that classes are much easier to understand, which is why I recommend that components/containers with a lot of state and lifecycle dependent logic should remain class-based. And, again, for everything else, function components are a better choice.

Thread Thread
 
leob profile image
leob • Edited

Thanks for elaborating!

Strange enough, most of what you say seems to confirm that in fact class based components are easier to understand, and that hooks can get tricky - however, then you surprisingly conclude by saying "for everything else, function components are a better choice" - but not really explaining why they are a better choice.

So the mystery for me remains why everyone is jumping on the hooks bandwagon, the only reason I can come up with being an aversion against "OO" (classes) and a love for "FP" (function components).

Can I make a guess at what would be the real reason, or a good motive, for favoring function components and hooks?

My theory is that, yes, class based components are way easier to understand when you have a complex component where you need to manage a lot of state, however the point is that you should not create complex components where you manage a lot of state - you should break them up into smaller (and potentially reusable) components, and hooks (and function components) encourage this.

So I think that hooks only pay off when you embrace a fundamentally different way of thinking and , if you don't do that and you prefer to stick with larger "monolithic" components then you're better off just sticking with classes (this is in fact also what you're hinting at).

Thread Thread
 
puritanic profile image
Darkø Tasevski

you should not create complex components where you manage a lot of state - you should break them up into smaller (and potentially reusable) components, and hooks (and function components) encourage this.

This is basically what I wanted to say above 😄 But in a case where you can't avoid a lot of state in a parent component, it's better to let it be a class and all their children to be functions (with hooks if needed). One scenario that comes to mind is when you are expecting to work with some data from Redux which is then needed in children components, it's maybe better, in my opinion, to have class component connected to redux instead of using redux hooks in children. This has some problems on its own of course, as we need to take care not to cause unnecessary re-renders when the parent state or redux state is updated.

Just to be clear, I have nothing against OOP nor FP 😄 I'm using both paradigms in my code, as each one has its own use case.

Thread Thread
 
leob profile image
leob • Edited

Right, yes all of that makes sense ... sometimes you naturally just need a component that manages a larger number of fields and breaking it down doesn't buy you anything. And replacing that with two dozen "useState" calls doesn't look like that much of an improvement over doing it in the conventional way.

Your remark about Redux is spot on, I came across two articles explaining the issues when replacing Redux with its "connect" API with hooks:

staleclosures.dev/from-redux-to-ho...

Quotes - regarding when you want to move from Redux to the "Context" API with hooks (useContext / useReducer):

"Without relying on Redux you lose out-of-the-box performance optimizations ..."
"Optimizing rendering performance is now your job"

And:

itnext.io/how-existing-redux-patte...

Quote:

"When you move away from connect you lose a lot of the performance benefits it provides. This means that you’ll have to be more cautious when considering re-renders and passing data from smart and dumb components"

(core issue here: "connect" does some smart things that result in less re-rendering out of the box and which you would have to manage yourself with hooks)

By the way I see now that these are really 2 different topics, the first article is about replacing Redux with useContext/useReducer, the second is about replacing Redux 'connect' with the new Redux 'hook' API (useStore, useSelector, useDispatch and so on)

But the conclusion with either was that you had to to put in extra effort to get the performance which Redux with hooks gives you 'out of the box'.

Anyway, the pattern where you have a "master" (class) component with Redux/connect and then a bunch of stateless child components (with hooks if needed) probably makes sense. It's almost as if the master/parent component is the "controller" from the classical MVC paradigm? :-)