![Cover image for A Hooks-vs-Classes Report Card](https://media.dev.to/cdn-cgi/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3t9d1nzseos04mnud3i6.jpg)
Hooks have been out now for a year-and-a-half. Maybe I'm late to the game, but I've only been using them heavily in the last few months. Part of ...
For further actions, you may consider blocking this person and/or reporting abuse
Great article! In my opinion, hooks are great because of the shared state and useContext() hook. However, I've spent countless hours trying to recreate lifecycle components, which I could have done in 10 minutes with a class-based component. useEffect() is a pain to work with.
Ultimately, I use global state more often than I need a specific lifecycle, so I go with hooks more often, but I can see why one would choose classes.
Absolutely. This is probably the primary reason why I've switched most/all of my new dev to Hooks. They have some definite... drawbacks. But the positives can also be powerful.
Amen. Amen. And, oh... did I mention?? Amen.
You can easily use Redux for state management ...
I could also use a sandblaster to wash my hands...
hahaha very funny :) it depends on how complex your application is going to be . Context is for low-frequency updates and simple Alpplication , but Redux is the most powerful solution for complex application state .
I understand that if you're a "Redux guy" then you're probably gonna continue to be a "Redux guy" - probably for a very long time. But with the improvements to the Context API and the ability to use shared Hooks, I don't honestly understand why anyone would ever want to inject the megalithic monstrosity that is Redux into a new project. I've written several articles highlighting my not-so-subtle feelings about Redux.
I understand also that if you're "NOT" a "Redux guy" then you're probably gonna continue to be "NOT" a "Redux guy" - probably for ever :)
Peace .
I like what you've written here; I'd like to contest your assertion around the SRP and the 'golden function rule' though - and then I'll agree with your assertion too because I'm double minded like that π .
I've written some F# code, specifically with the Suave web framework. In that framework, it's idiomatic to use functions to compose a type called a WebPart to build up functionality of your app. The 'root' webpart would technically be the entire API/Webserver which is certainly 'not a pure function' that does one thing well. However, isn't
main
'just' a function too?main
(or index) can be written well by delegating and composing the app as it should do since its the conposition root, orpoorly if main triess to do far too much all at the top level.
So at some level, there are higher order functions whose responsibility is to compose smaller, purer functions.
Can't the same be said of react's functional components? Each component delegates to hooks and sub components. This is analgous to the web part type in my mind, and indeed static void main and composition roots.
That being said, your point is still very valid. Without really good names, coding discipline, or true functional programming and coherence (like Elm/PureScript/sub-set of F#), functions in JS can just become big fat classes. Heck, what is the responsibility of those IIFEs we had to write 'back in the day' that our bundlers are writing for us now?
Great food for thought! I'm gonna have to see if I can't name my components better to reflect their reaponsibilities.
Excellent feedback! And I totally agree with everything you've written.
My point isn't to say, "Functional components don't sound like functions - so you shouldn't use functional components." My point is just to illustrate the contradictions when a Functional Programming Acolyte (and Certified Class-Hater) tries to yell you down because those horrible, nasty, unconscionable class-based components don't comply with their "pure" vision for functional programming in JavaScript.
Even in the best OO codebases, there are some classes that look-and-feel a whole lot like... functions. (
static void main
is an excellent example of this.) And even in the best FP codebases, there are some functions that look-and-feel a whole lot like... classes.When we come across those scenarios, I think it's absolutely healthy to run the "coding bromides" through our head and say, "Hmm... Have I really architected this application properly??" But I know full-well that, sometimes, as an experienced developer, you will run that "check" and come to the conclusion that, "Yes... This really is the proper way to go about it."
One of the first steps in learning to program is to learn "the rules" - whatever those "rules" may be for the particular flavor of tech in which you're building solutions. The "rules" are there for a purpose - and generally speaking, they're usually quite helpful.
But the process of becoming a well-experienced programmer also entails learning when it's appropriate to break those "rules". For example, I've written-and-deployed individual functions that contained hundreds of lines of code. Normally, that's a "code smell". But there are absolutely some times when it's 100% appropriate.
This often comes to mind when I'm writing/demonstrating a React class-based component and some kid (sorry if that sounds dismissive - but it's often quite
TRUE
) says to me, "Dooood... You shouldn't be writing classes in JavaScript." And I gotta admit that, in those scenarios, my inner (snarky-as-hell) "developer voice" is thinking, "Yeah... How bout you just stay in your lane, kid???"I also wanted to harp on this (excellent) statement from you, because I believe it highlights a broader point that I'm trying to make in this post. (And indeed, I think this point permeates many of my other posts as well.)
Part of being a programmer is the mindset that a Given Set of Inputs/Logic X should always (or, ideally) result in a Given Output Y. In functional programming terms, we call this a pure function. Taken to a greater extreme, we tend to think that everything can ultimately be coalesced down to a Boolean.
I'm not "angry" about this mindset. I have it. Nearly every solid developer I've met also has it (at least, to some degree). But this mindset also leads to potentially harmful dictates in the development community.
Some "thought leader" comes to believe that a particular programming pattern is "bad" or "harmful" or a "code smell". They start writing think pieces about it. They start proselytizing about all the reasons why Pattern A is "wrong" and Pattern B is "right". Before you know it, people are writing additional libraries with the underlying intent of banishing Pattern A.
But everything in programming (or, in life) isn't always as simple as
classes === FALSE
orfunctions === TRUE
. You can hold two thoughts. And if you're holding those thoughts, it doesn't necessarily mean that this is a problem to be solved. It's entirely possible that classes might, sometimes, beTRUE
. And functions might, sometimes, beFALSE
.So... even though your lighthearted acknowledgment may seem (to some) like a throwaway comment, I think it illustrates the deeper knowledge that experienced devs can bring to the table when we decide to wade into the debates about whether Pattern X is "good" or "bad".
Thanks for the article. I enjoy reading your POV!
I fall on the other side of the discussion concerning functions (Hooks) vs classes in React, but I've got nothing against classes. I use them in other projects but not React since the Hooks API (IMO) is much simpler than the classes one. Sure, developers sometimes still make messy code (me) that's hard to read but I believe that Hooks give you a better head start to make things simpler. Plus, packaging up state management in a custom hook for reuse is a great API that I use all the time.
Thanks again for the article. I appreciate the effort you put into it. It's super important to have these discussions and I look forward to more.
Cheers!
Thanks for the feedback! I'm genuinely curious about this statement:
How, precisely, have you chosen to do that? I'm asking because I wrote an article highlighting a particular technique that I've discovered (dev.to/bytebodger/hacking-react-ho...). But I've also found a lot of misinformation around this topic. When people make statements like this, it often sounds (to me) like you can just create a custom Hook that uses its own state, and then you can just use that custom Hook wherever you want, throughout the app, to share that state - but... it doesn't work that way. And I rarely hear people talking about the specifics of how to make that work.
So I'm genuinely curious to see the approach you've taken.
Good question. I'll do my best to answer below.
It's key to note that Hooks do not solve Global state management. They're just a different API for state management in general, whether that be local or global state. If you want, you can use context, prop drilling, etc for your global state management. But Hooks were not made specifically for the global state problem, just state.
So what do I mean by packaging up state management for reuse?
I can (potentially) reuse that custom hook in another project without changing the code. I say potentially because it depends on how well the hook was written in order to make it reusable in another project.
This has two potential benefits.
As far as sharing global state deeply into your app , that's still the same as ever. Either use prop drilling, context, or some library like Redux.
By no means am I saying that Hooks are perfect, but IMO they're a step in the right direction.
As far as rendering state deeply in your hacking-react-hooks example you could use the children API.
Excellent response - and sincerely appreciated! When I look at the example you've given with the children API, it strikes me as "I'd do this with Context". This is not to imply that your example is inferior or that mine is "better". Just different paths to the same result.
In some ways, your response has also further-solidified what is (IMHO) a central "issue" with Hooks. Or maybe it's just an "issue" with JS devs. I don't know.
But the "issue" I'm talking about is that, when I've heard people describing Hooks, I've become attuned to the concept that they're almost speaking a different language. A "Hooks person" will often say "Oh, I use Hooks to share state" and "non-Hooks person" will hear that and have an entirely different idea about what the "Hooks-person" was trying to communicate. Then the "non-Hooks person" sits down and starts playing with Hooks - and the constructs don't seem to support everything they were hearing from the "Hooks-people".
To be absolutely, 100%, crystal clear on this, I am NOT saying that this is some kind of "fault" with Hooks, or with "Hooks people", or with React in general. It just seems to be, IMHO, a strange dichotomy that has been building in the React community. Neither side is "right" or "wrong". But it still feels to me like... a problem. And I have no idea what (if anything) can be done about it.
This is what I was trying to get to in the "Developer Cognition" part of the article. I don't honestly think that either "side" is right or wrong. It just feels, increasingly, like there are two different... dialects being spoken.
All that being said, I think the examples are excellent and they've given me some good "food for thought". I appreciate the time!
I couldn't agree more. That was the main concept under the "Developer Cognition" section. And that's definitely part of my inspiration to write an article like this. I don't like any environment (which is all-too-common), where they have made some universal dictate like Thou Shalt Not Use Classes or Thou Shalt Not Use Hooks. Unfortunately, devs often get polarized on such issues.
Yeah... I kinda referenced this in the "Legacy Compatibility" section. I haven't (yet) had any real problems with Material UI (and I use it all the time). I think this is because Material UI gives you a bunch of self-encapsulated components and then allows you to drop them into your own components - whether those components are functional- or class-based. When it's done like that, the functional/Hooks components really play quite nicely with classes.
It's kinda like TypeScript. I'm not using TypeScript in my dev. But many of the packages that I use are written in TypeScript. And for the most part... I don't care. Because all of their behind-the-scenes TypeScript doesn't affect how I use the package in the middle of my components.
I have, however, run into the exact problem you reference with GraphQL. In the latest/greatest Apollo library, they want you to reference the Hooks directly as a means to use the package. In those cases, you either convert your classes to functions (yech...) or you don't use the library at all. (Or you write some horrific, bloated wrapper function that allows you to leverage the Hooks from inside a class - also... yech.) I'll freely admit that this annoys the crap outta me.
I actually ended up scrapping Apollo altogether. But not exactly because of the "Hooks problem" (you can get versions of Apollo that play along just fine with classes). My "beef" with Apollo (and other GraphQL libraries I tried), is that they want you to hand control over the API calls to the render cycle - which I find to be completely untenable.
I wrote a post that references this broader issue here:
dev.to/bytebodger/react-s-odd-obse...
I ended up dropping all GraphQL libraries and I just wrote/formatted the GraphQL queries manually in my API components. Afterall, GraphQL is nothing more than a specific way of formatting a query (i.e., a string). And I realized that I didn't really need an additional library for that.
Yes and no. Sometimes I write some... "cantankerous"-sounding shit. But it doesn't mean that I've soured on React as a whole. In fact - I still love it. I just get frustrated sometimes by some of the forces that are driving it in suboptimal directions.
And I'm not a "Hooks hater". In fact, I've kinda switched most of my new dev to Hooks. Yes, they have problems. But they also have some really strong benefits. So it's a give-and-take.
Also, when it comes to new libraries/frameworks/paradigms/etc., like most devs, I'm always looking at other alternatives with genuine curiosity. But I also really enjoy the process of getting a paycheck.
It's one thing to say, "Oh, man! I just found this brand new library/framework/paradigm/etc., and it's friggin awesome!!" It's an entirely different thing to take that back to your team (or your company) and say, "Here's why I think we should totally abandon all that React stuff we've been doing and adopt this Hot New Thing."
Good series! I'm really enjoying your POV ππ
Thank you!