I must be the only person in the world who likes class components in React.
Sure, it's often overkill and a functional component does the same thing with less code. Use a functional component in these cases. But if you're doing something more complicated then stop treating class components like the fucking devil. They have their place.
I ditched React soon after they released hooks, mainly because I couldn't relate to the tradeoff React roadmap was taking from there. They went in a different direction from that point onwards, it seem like whatever code you write will become obsolete with the new set of best practices in the next release cycle.
More importantly, I realized React is trying to tame Facebook level of problems and hence their design decision steams from those data points. I develop small/medium type of frontend apps which doesn't need to apply solutions developed for such a scale.
These days I'm using Hotwire[1] (Turbo + Stimulus) with fair amount of vanilla javascript libraries in my apps. Occasionally, when I need to develop a reactive piece of UI I reach out for Svelte[2]
I'm quite happy after making the move. My apps complexity has reduced drastically and there is huge boost in my overall productivity.
Step 1: The existing tooling is too clunky, big and a major PITA to work with, Developers spend most of their time fighting their framework and tooling to do simple things.
Step 2: Someone gets fed up with this writes a framework that "does things right" and is designed for "simplicity"
Step 3: People start loving the new tool because it is so much easier to work with.
Step 4: People start to do things the tool wasn't designed to do. They start making "minor feature enhancements" to the tool so that it can fit more use cases.
Alternative Step 4: Tool becomes "industry standard" so everyone starts using it because it is "the right way", regardless of whether or not it is a good fit.
Step 5: The new tool becomes massive and bloated and overwhelmed with too many features, configurations, and options.
Step 6: Return to step 1.
> The Wheel of Time turns, and Ages come and pass, leaving memories that become legend. Legend fades to myth, and even myth is long forgotten when the Age that gave it birth comes again. In one Age, called the Web 2.0 Age by some, an Age yet to come, an Age long past, a wind rose above the great mountainous island of FANNG. The wind was not the beginning. There are neither beginnings nor endings to the Wheel of Time. But it was a beginning.”
I have been doing Rails development for past 10 years now and I never faced a dilemma where the framework took a direction which isn't aligned with its core vision.
I have been just trying to find a similar tool for frontend where I don't have to keep rewriting the entire codebase.
What about the webpack rails7 situation? I feel like rails has guessed wrong too many times about FE (coffeescript, asset pipeline, websockets) that I don't trust them to deliver their own stack.
I'm not sure this is a fair representation. Rails didn't "guess wrong" too many times. They chose the best option available at the time. Remember, CoffeeScript was used in Rails in 2010! Brendan Eich even borrowed many ideas from CoffeeScript in subsequent versions of JavaScript. Ditto, the asset pipeline solved a great number of problems for many years. With recent advances in web browsers, many of these problems aren't really problems, so it's less relevant, and Rails has to take advantage of import maps as an alternative.
You are correct, Rails and Ruby are one of the few places I haven't felt like there's been constant and unedning churn, I attribute it to the incredible flexibility and power of the Ruby programming language, which incidentally is my favorite language to work in.
I think Rails was able to avoid this because of its modular structure and ease to write gems that extend the framework in a way that fits your project's circumstances and needed tradeoffs. As DHH famously said Rails is omakase, but you can also make reasonable substitutions.
> The Wheel of Time turns, and Ages come and pass, leaving memories that become legend. Legend fades to myth, and even myth is long forgotten when the Age that gave it birth comes again. In one Age, called the Web 2.0 Age by some, an Age yet to come, an Age long past, a wind rose above the great mountainous island of FANNG. The wind was not the beginning. There are neither beginnings nor endings to the Wheel of Time. But it was a beginning.”
In case you don't know. This is a variation of the first paragraph of the Wheel of Time book series by Robert Jordan. Each book opens with a variation of this same paragraph
After a few trips around the wheel you'd think that people would realize "hey, we rewrote our entire react app, we were good little developers, we did the right thing,.. but that path led nowhere"
This tired argument has been trotted out repeatedly, and isn't really funny anymore. It's just old and boring.
For people like me who are new to JS and use these sorts of discussion to determine which technologies I should adopt first, it's really just pointless noise in the channel.
The little I know about JS so far, I definitely know this joke is tired and played out.
It's not a joke, it's intended to convey the wisdom of not thinking "the next big thing" will solve all your problems, it is intended to focus on the importance of fundamentals, and of relying on using engineering skill to solve problems rather than fancy gadgets, it is to point out the value of developing technical acumen rather than becoming a tool bound technician.
If you define yourself by the technology you work with you aren't an engineer you're a technician.
I understand your frustration, but I think you’re missing what is actually meant to be a helpful takeaway from this.
Essentially, all these frameworks ARE part of a cycle that repeat, and in time we all start to see it. Most of them just iterate on previous concepts, with some core concepts going way back to the late 60s, 70s and 80s.
What remains the same is the underlying Javascript language itself, and more importantly, fundamental software engineering and computer science principles.
Learn the fundamentals, and they will help you learn every new framework and language more easily, and in more depth.
If you really just want to get a job using JavaScript, then you might be best off browsing job postings to see what kinds of companies are hiring for which frameworks, and go from there.
But if your goal is literally to learn JS and find the right tech stack for you, unfortunately there is no replacement for learning the basics, and then picking a framework that looks interesting, and building something in it to see if it clicks with you.
adopt better methodologies, languages (TS, ReasonML, etc), find what works for you, automate the hard part, find reusable patterns, don't settle for the popular but mediocre new stuff (eg. React)
> I realized React is trying to tame Facebook level of problems and hence their design decision steams from those data points.
Strong agreement!
There's a saying in community organizing and activist circles that goes something like:
"Don't apply the solutions of the butterfly to the problems of the caterpillar."
Imho it's a really important consideration when thinking about scale of movements (and nonprofits... and any community initiative...). This is especially true when "professional" people are always coming in and confidently over-applying their learnings in corporations to thinking about activism (which often, though not always, has very different incentive structures and lifecycles).
I think about this parable often in regards how all these scaled tech companies end up stewarding
the developer tools and therefore practices for everyone. And they tend to out-pace and out-broadcast other wonderful tools that could better serve the majority of developer niches.
I don't have a strong opinion either way but if you left React because you are frustrated with code becoming obsolete and picked up Hotwire, a significantly smaller framework, arnt you likely to run into the same problem if Hotwire doesnt end up being the next big framework?
In 5 years if no one else is using Hotwire then your code is obsolete, no?
I liked working in next.js in the past but my company was already talking about Nuxt.js when we hadnt even fully converted to next. Feels like jumping ships from a ship with some major flaws, but will continue to float for x+ years, onto a lifeboat that may or may not make it another 500 feet before sinking.
I think that's the opposite of what he is saying. Because React is updated and changes frequently, your code becomes obsolete unless you update to follow the latest changes. In theory, if everyone abandoned Hotwire, your code would never be obsolete or need changes, because Hotwire would never change. In reality, as browsers and web standards change, frameworks need updating.
React doesn't change frequently though, that's the point. And it has always had excellent backwards compatibility. Class components are still fully supported, for example.
React changing best practices every few years is a good thing! It means it keeps getting better.
That would be a bad thing if it meant that earlier best practices were no longer maintained. But that’s not true. Class components are still around. All official documentation continues to have class component examples and explanations (where it makes sense obviously…there isn’t any class component documentation under hooks, of course). And class components will likely still be around 5 years from now.
All the class components people wrote still work and can be maintained. React doesn't overhaul everything like Angular did. You can have function components with hooks and also have your old class components in the same app.
I liked classes to-- their lifecycle methods were so clear to work with.
That said, learning how useEffect replaces lifecycle methods is pretty quick to pickup. As is useState.
The lesser known hooks are what are difficult for me to understand. useRef is pretty clear. useMemo? I'm not quite sure what it's for, but I imagine it's not too tough to figure out if I spend a bit of time learning & experimenting with it.
So, although I liked the simplicity of class components, functional ones aren't too bad. They bring in some additional complications, but with a bit of effort it seems to all be learnable to me.
> when I need to develop a reactive piece of UI I reach out for Svelte
I never used Svelte, but I feel that "develop a reactive piece of UI" is basically Stimulus' job description. Can you explain a scenario/task where Stimulus does so poorly, that you flip the switch and use Svelte instead?
Stimulus was made by people who hate JavaScript and deliberately and pointlessly avoids JavaScript conventions. If you hate JS, you might like it, but I like JS, so I hate Stimulus. My preference is for Alpine.js, but Svelete is cool too.
Howard Lewis Ship kept changing the paradigm for Tapestry every major release or two. He could have had something to take the spot that Spring eventually filled, but these big gear shifts I think left people with a lot of rework and at some point it seems like porting your code to something more architecturally stable is a better bet.
It’s challenging though to keep supporting an API contract that you don’t believe in anymore. Or especially that you are causing you and others pain. I think sometimes maybe the thing to do is pass the baton and work on something else, but we also punish people for abandoning things. Like the author of Groovy, who hopped around from project to project and lost people.
Because HR (and IT strategy) nowadays is a farce, managers, decision-makers and the actual organizational units that do hiring rarely have the necessary systemic big-picture knowledge/information, foresight, experience, longitudinal thinking that would allow them to venture out of their preset suboptimal ways.
The worst experience so far was for a general full stack role. They sent me a 3 page long PDF of job "requirements" which was a lot of fluff and implicit stuff. I get it... but I think that can be reserved for later. Not the best offer, seemed like they were disorganized in general, etc.
The best experience was for the best TC offer _and_ most relevant position for me... they sent over half a page that didn't waste a single character. Named a few frameworks but they had a crystal clear idea of what they needed. It was amazing, lol.
It's usually perfectly reasonable, but not really optimal. In this job market requiring React seems worse than hiring someone that is a good frontend developer and has the wits and eagerness to pick up React and whatever tools the team uses, and the job requires.
This has the advantage that when - inevitably - tool1 gets deprecated and tool2 arrives you'll have someone that can adapt and learn, and likely you'll also have someone who can teach.
Nope, I'm with you. React inventing a half-baked, partial re-implementation of objects/classes (in a language that already has them!) with super-weird declaration syntax & runtime behavior, just to avoid telling their userbase "Ok you will have to use classes sometimes, for certain functionality", was when I started looking around, because they were clearly out of real problems to solve that were sufficiently good-looking-on-a-résumé, and it was just gonna go downhill from there.
* SHOW HN: UberTinyUltra.js Here's my new super light-weight 4k Javascript Library!
* How I switched to UberTinyUltra.js from PopularFramework.js and simplified my life!
* SuperCoolStartup switches to UberTinyUltra.js
* How I built my unicorn on UberTinyUltra.js
* UberTinyUltra.js 2.0 now with a compiler, classes, typesafety and a C++ like turing complete template language to take on the big enterprise jobs because we have too much time, attracted a lot of developers to the project, and ran out of obvious simple features to implement!
* I hate you UberTinyUltra.js you are too complicated.
* ShowHN: SuperTinyMega.js Here's my new super light-weight 4k Javascript Library!
Often, things don't get bloated because the developers are bored, it stems from users requesting features because they are doing it wrong.
Sort of how AWS keeps mucking with Lambdas so that thier customers can keep doing it wrong and paying too much.
Or how doctors just give patients scips if they ask about a drug and it won't incur them any liability.
Over and over I have seen the world shout the praises of a new paradigm that solves a problem...except they totally misunderstand the concept and run in an unintended direction. No one can fight that momentum. So they don't. It happens with nearly everything in tech.
I still remember the Parcel or Prettier saga. The first, the no-configuration bundler, the other, the no-configuration opinionated code formatter.
Then, for their next big major release, the big feature was adding configuration to make it even more powerful!
I haven't used them in a while so my memory might be hazy, but this is exactly the phenomenon you've described. I wonder if it's insecurity or trying to please the crowd that simple niche tools in the frontend end up being configurability behemoths. There's always a time in a JS library where someone decides to rewrite the whole thing and add plugin support, and that's the time that library goes to shit.
Over long enough time you get Webpack, with more knobs than the Linux kernel, and cycles back to a version that tries to be smart and not require any configuration anymore, like 1.0 did.
Well. Life moves in waves, but historically, over a long enough time frame (and if we manage not to kill ourselves), more lessons will be learned than lost, and life gets increasingly better.
Oh, absolutely. Pretty much every distinctive feature of prototypal OO is best classed as "please, never ever actually use this". Adding the "class" sugar and general agreement to pretend the prototypal stuff isn't there, though, made JS OO usable-enough. But yeah, it's not great.
This thread may have an OP who prefers classes, but I'm for one 100% in favor of hooks. You could struggle and suffer to solve them with classes, or you could move on to something better.
It is purely because `this.state` is hard for V8 to optimize, nothing more and nothing less. They did it for their own purposes, for better performance on low-end machines. You can almost certainly just use classes for 99% of use cases
Additionally, the React team has talked about how hooks and function components allow them to have "forks" of the component tree in various states of partial completion at a time, for use with Suspense and transition features. This works because closures capture data in a scope, whereas mutable class instances could have gotten changed and refer to a single `this` instance.
One of the few reasonable comments in this thread.
Hooks are better than HOCs, no this is better than managing this, and dependency arrays are better than if (this.props.fooBarBaz !== newProps.fooBarBaz || this.props.onFooBarBazChanged !== newProps.onFooBarBazChanged || …)
> This works because closures capture data in a scope, whereas mutable class instances could have gotten changed and refer to a single `this` instance.
So `this.state` is hard to optimize?
> Hooks had nothing to do with v8 optimizations or `this.state`.
Huh? That's what you just said. The spec makes it hard to optimize. The JIT compiler can't infer enough about the shape of the data.
It's not about "optimization" in the sense of "how fast can the v8 interpreter execute lookups on the class instance" or anything like that.
It's about "when this function executes, what data does it see, and did any of those values get changed out from under it in a way that could potentially break the logic"?
> It's about "when this function executes, what data does it see, and did any of those values get changed out from under it in a way that could potentially break the logic"?
You're just describing the semantics of functions versus classes, and the consideration has existed long before hooks came around for exactly the reasons you describe. This discussion has also existed long before JavaScript, React, or hooks existed obviously. It's secondary to the discussion because function components have existed for a long time.
None of what you're saying changes the fact that replacing classes and lifecycles with hooks was done despite the fact that 99.9% of the API users would never care about the performance implications of classes, while that is precisely why the React team made the change. JavaScript is plenty composable with classes and without nested useEffect calls, which is one of the most absurd foot gun APIs that I have seen released in my entire career.
I'm legitimately curious. Can you point to a single actual source to cite "the React team made the change to hooks because of JS engine performance"? (and when you say "performance", you _are_ talking about literal instruction execution time / JIT behavior / etc, that sort of thing?)
I'm pretty deeply tied into the React ecosystem, and I honestly can't remember _ever_ seeing that mentioned as a justification.
If you can link me a reference with them saying that, I'd both be interested in reading it, but honestly surprised to see that stated at all.
Funny that this whole hooks mess could have probably been better resolved with investment in developing optimization patches for v8 rather than attempting to "fix" React
How would Facebook convince the community to dogfood their new API without some marketing buzz? It's pretty obvious that V8 has trouble optimizing changes to object properties because of the design of the spec, and they were also pretty open about it in the blogs where hooks were introduced. I think it's also clear that useEffect is harder to grok than it's lifecycle equivalents, so there might be other things at play than composability.
It's also a bit funny that certain kinds of people can just brush off these things with some non-sequitur on a message board when there are almost always bigger things at play.
I was just going off my own experience with React through the years. It was very hard to compose behavior with class based components. We had to create higher order components that had functions as children which could pass down data as arguments to the child function. It really made a mess of the component hierarchy. Now you can create reusable custom hooks that can be used from any component.
Mainly Vue. It's OK and pretty widely used, which gets you decent library support and such. If I were starting from scratch and didn't have organizational/team inertia & experience to consider, I'd probably give things like htmx a hard look.
I'd have to re-evaluate the current Android landscape because it's been a while, but I might still consider React Native only for that platform, because having RN smooth the rough edges off Android dev made it a lot more pleasant and quick, with far less time lost fighting the platform and working around/replacing bad 1st party stuff piecemeal. But maybe it's gotten better-enough in the last 4ish years that it's no longer tempting to drag in RN just to avoid native Android dev.
In general though, I just no longer default to advocating React for highly-interactive sites or "webapps", as I did for a while.
They absolutely do not have their place. Just about everything is worse with class components. I’ll take a dozen useEffects over a single class component any day.
That said, the hooks model is far from perfect. They give you a lot of rope to hang yourself with and were badly introduced. Within weeks the internet was ablaze with terrible advice.
When so many people get it wrong, the library is to blame. And I wish hooks were as robust as Solid.js’ signals model.
I don't think hook themselves are a bad thing. But the way react implements hook probably is. The react team invented their hook format the suite themselves the most, but that probably isn't for other.
Vue 3 also has hook now. But none of these defects in the article exist.
In vue.
To use some value in a effect, you just use it and it is tracked.
If you need to clear up your code. You cut some code into a useXxx function, and your reusable utility is done. You can use it everywhere now. You don't really need to specify dependency of whatever by yourself.
If you must manipulate a dom, you just manipulate it. It will work as long as you revert the change before vue want to update it again.(And vue do provide a hook for you to attach a handler when these happen)
These restrictions are added by the way react implements hook, but not hook themselves. Hooks are just more user friendly services (by allow the hook to attach to component instance without all the glue codes(addListener or whatever))
> Vue 3 with the composition API looks fantastic on paper. (I haven’t used it in any meaningful way)
Recently worked on migrating a large codebase over from a legacy solution to Vue 3 with composition API, it was a pretty enjoyable experience, especially with Pinia!
Though I'd say that the problem was that most component libraries/frameworks out there actually don't support Vue 3 well enough. Last I checked, only the following were viable with Vue 3:
- Ant Design
- Element Plus
- PrimeVue (went with this, also has icons and a layout solution; also alternatives available for React and Angular)
Most of the other ones considered weren't quite there yet:
- Vuetify
- Quasar
- BootstrapVue
- Vue Material
- Buefy
- View UI
- Vuikit
- Chakra UI
I mean, is it really so hard to get a bunch of pre-built components for Vue 3 with Bootstrap, or Bulma? It feels a bit like the Python 2 to Python 3 migration in some respects.
> Most of the other ones considered weren't quite there yet
From what I recall, quasar managed to update very quickly.
Vuetify is currently in Beta for Vue 3 as they went with a greenfield rewrite to deal with tech debt. I rolled it out to a production site recently and it works well.
BootstrapVue completely dropped the ball, despite the corporate sponsorships the original maintainers went missing for a whole year & bailed. It got new maintainers at the start of the year, but they've been delayed by current geopolitical events.
I used it in a few project. And it reduces the code by a lot.
For example. To make a mutex to prevent duplicate submit of form. You use need to declare a instance field and wraps the code that do the submit. That alone is 3 lines of code but don't even include error handling.
Instead of a clear message about what caused that loop and maybe a walkthrough of the last N iterations (which changes caused which logic to be triggered), the error message was just a vague and lazy error (even AngularJS errors were better back in the day, filling in details in the docs as URL parameters).
Then again, over the years I've formed the opinion that both approaches are bad, just in different ways. There is no silver bullet, just look at how even desktop software and UI solutions struggled to be workable throughout the decades, of course things won't be that much better in regards to front end.
However, Vue's approach to hooks seems refreshing: creating and nesting components still isn't as easy as in React, but getting things done feels a little bit less cryptic and not as confusing, especially with something like Pinia!
Class components are great. One thing I did see at my old job, however, was people extending the class components, sometimes 10+ times, which was very unexpected and harder to reason about. With functional components and hooks, you enforce proper composition.
Hooks are a natural progression from class components, class components seem more straightforward with all the willMount beforeMount etc. but they trick you into visualizing your component in different states of mounted/rendered/etc. That goes against what React wants to be at its core, which is totally immutable and pure, it isn't mounted or pre/post-render it looks the same whether it is the first render or the 100th. Ultimately I think the mental model of the component as a pure function is superior to juggling the react lifecycle.
Class components were/are fantastic for training new devs because of how easily abstracted the mental model is compared to memorizing all the hooks and their use cases. Obviously, the syntax tends to be a bit more verbose in general, and I won't deny that hooks making improving performance easier, but at the end of the day I prefer code that's readable whether it was written yesterday or 5 years ago - even if it costs like 20 more lines of boilerplate.
I also think the fear of verbosity in this post is why I can't take it too seriously. Solo maintaining a not-so-old Expo application which uses React Navigation, I constantly have to rewrite large sections of code due to constant refactorings to match whatever coding style is hot at the moment, as the 6 month deprecation deadline is constantly at my doorstep because I have bigger fish on my plate. SO answers being out of date isn't a problem because React is too old, it's a problem because of everyone wanting it to be shiny and new again, and React acquiescing to those demands.
The author does have a bit of a point with libraries, but I counter with Nessim Btesh's fantastic article regarding proper React style[^1]. In short, you shouldn't really be depending on libraries for production needs, just for prototyping.
How so? There is nothing that a class-based component can do that a hook-based component can't (EDIT: Except for error boundaries). I'd go as far as to say class-based components are strictly inferior because they force you split your behavior logic across lifecycle methods and are not easily composable. I've been able to support much more complex behavior easily with hooks (e.g., connection management), that would have been a nightmare with class-based components.
I don't know that I agree with that. I recently had to fix a few bugs because some devs forgot, or simply didn't know, that lifecycles still exist.
We have reached a period of time where junior devs exist that did not cut their teeth on class components and have no idea of what the lifecycles are. They don't know how rendering works so they either throw up useless memoization everywhere or they don't bother to think of it.
The complexity of class components did not vanish. It was only swept under the rug. Take useEffect. It's overloaded to hell and back. It does too much and people continue to struggle with it. Another one, useRef. It's not really for refs, per se. You will end up using it for data other than refs because function instance variables aren't a thing. No one knows when to use useLayoutEffect or useCallback.
The fact of the matter is that it's not class components vs. hooks. It's that React's API sucks. It's always sucked. And it continues to suck.
> I recently had to fix a few bugs because some devs forgot, or simply didn't know, that lifecycles still exist.
And then it just stays fixed. You don't have to split up the logic across different lifecycle methods and remember to keep them in sync. They're consolidated into functions that can be trivially pulled out into their own hooks and reused.
React is definitely a more low-level framework that requires you to have some familiarity with how the scheduler works, and could benefit from a couple more built-in hooks. But the benefits are undeniable, they make writing and refactoring complex applications an absolute breeze. The fact that you can factor out some state-related behavior into a hook and trivially reuse it is incredible.
For example, take state. With hooks, you use useState. A very common pattern is to need to persist it in the URL to enable deep linking. You can literally just replace your useState call with useQueryParam from the use-query-params library [1] and have your component function identically. You just can't do that with class-based components.
Oh yeah, that's the worst. Since logic related to a behavior must be split up across multiple methods, it's really hard to tell if you've actually implemented it correctly. With effects, the setup and teardown is in the same function and can be moved around as a unit.
> I'd go as far as to say class-based components are strictly inferior because they force you split your behavior logic across lifecycle methods and are not easily composable.
That's not strictly true. You can implement "hooks" on top of a class based components. It's essentially the strategy pattern.
No, you're not the only one. Apart from useState, which is elegant, I hate others with passion. How can a replacement for lifecycle methods be called useEffect? Seriously?
> How can a replacement for lifecycle methods be called useEffect? Seriously?
Yes, seriously. Have you used it for more complex components? You can split your effects across multiple useEffects, and have a guarantee that they run completely independently of each other (especially since you know what their dependencies are). Compare that to lifecycle methods: you only have one per component. All your effects concerned with setup have to be piled into the same function, and their associated teardowns have to be elsewhere in another method. How do you audit a behavior's logic? How do you reuse that behavior in another component? It's not easy, and you can cause weird bugs depending on the order in which you run them.
Compare that to useEffect. Setup and teardown in the same function, which allows it to focus on just one behavior. And if you need to, it can be trivially pulled out into its own hook and reused across all your components.
Fair enough. Though, the first question is - why does the component do so many things? Do they all need to be in a single component or can you break them apart?
For the cases when this is not possible, I agree, useEffect works better than classes lifecycle methods. But do you really want a system which caters to a small percent of use-cases at the expense of readability in others? And the improvement, to my eyes, is not that big anyway.
The hooks are my main gripe with React, but as the OP, I don't see a better alternative either - at least not one that would be worth a rewrite.
Yeah, it's not great, but that's because it's extremely general purpose. Its name refers to function side-effects. Since you're writing your components functionally, you need to explicitly declare any effects of your function to be run appropriately. Hence: `useEffect`. The names are super generalized because hooks don't necessarily need to render anything; they're just hooks into the React scheduler, so they can even be used to manage connection state if you want.
It's not a matter of doing too many things. Lifecycle methods exist to be used, and the moment you have any behavior that requires multiple lifecycle hooks, you will run into these issues. So I'd say it's not a small-percentage of use-cases at all, and you'll often see the benefits of hooks immediately.
I find hooks make it easier to break apart components. With a class component, there are tons of hidden dependencies on those huge components. Maintenance programmers add them relentlessly. When it comes time to refactor, it's often easiest to just translate to hooks and then start breaking things apart. Why? hooks are small and self contained by nature, and they compose nicely, so you can start with a ball of hair and use regular refactoring tools (extract to function, move to file) to decompose them.
That didn't require the meta language and rules of hooks though, they could have added this.addEffect(callback, deps) or something to class components. To preserve back compat they could have added a new base class you inherit from to get access to new APIs.
Most of hooks could have been done incrementally on top of classes.
Imagine trying to debug a component that both has lifecycle methods and reactive hooks? That would be a total nightmare, especially when the same state is being updated in both places (because that would have to be allowed). Going all-in on hooks means that there are two distinct, independent ways of writing components, and you never have to deal with interactions between the two.
I also don't think this would be possible. Hooks are based on closures and injected state, which you can't do in a class since separate methods won't share the closure.
Only if you're constrained by the existing API of hooks. Preact signals is a good example of how class components could have been improved with reactivity:
useEffect is poorly designed for sure. It's the single most common source of react bugs I've seen and most developers have to refresh themselves on it if they don't use it for more than a few months because it is totally unintuitive.
A lot of third party hooks are really good though, being able to hook a dependency rather than create higher order components saves a lot of time and is conceptually much easier.
I overall agree. I use functional style programming a lot but for some reason hooks have always confused me. The component classes generally make sense to me and map onto other paradigms like flutter, vue, etc.
Sure methods like componentDidMount are a mouthful but I found it much more explicit
In my view, hooks are more about managing state dependencies, rather than functional programming, per se. So they're more useful for simplifying methods like componentDidUpdate, rather than componentDidMount. When you need to detect changes by comparing prevProps to this.props or prevState to this.state, the logic can quickly get really ugly. Instead you can just put the relevant prop or state you want to monitor in the dependency list of a hook, and it will be triggered whenever that dependency list changes.
I totally agree. I actually stopped using React around the time hooks were announced. In retrospect it's still not clear if hooks were even a good idea.
Changing the core methodology of a project used by millions of developers at the time was extremely irresponsible. They basically made obsolete all React educational resources overnight. I'm sure people making money by producing React educational content were very happy about that though.
There was no way to make async rendering possible with classes. It was necessary and they spent a lot of time trying different approaches that'd continue the old paradigm, but it couldn't be done.
As opposed to what? Vue is slower and Angular too. Perhaps Svelte might be faster but the programming paradigm is a little weird. Writing pure JS is bullshit - I tried it few months ago and even a very simple app - for public transport schedules - got very unmanageable very quickly.
I'm currently working on a project with hundreds of reactive components shown at any given time (very extensive financial analytics/modeling collected from over 500 data sources and real-time updated). Async rendering is a godsend. Perhaps React isn't for you if you don't see the need.
Lol, you really think someone is going to stray off the well-supported path for apps like these for 1-10% gain? This app shows a loading indicator quite a lot. Speed is important but nobody cares about raw speed this much. What we care about is a well-maintained library with significant ecosystem that's still going to be there in 2030 and it's super-easy to find devs and/or get help. The libs there are beta quality at best - maybe in few years.
The point is I'm not going to compare against small libs with no ecosystem, slow maintenance, small community and low amount of developers ready to use it - these new libs sound nice but it's beta software, not something to be used in production for apps like I work on. Speed is important but not this important.
What remains is React, Angular and Vue - and out of these React wins by a huge margin, also thanks to async rendering.
I'm looking forward having another look at the remaining libs in a few years.
Just for the opposite perspective, I started using React when hooks came out and I love them. I can write the classes, I often have to for job interviews, but I think they’re a messy abstraction.
Hooks just match the way the React runtime works. Classes are just way easier to do weird bad stuff with. I’m
not sure I would ever choose React if it was still class based. I’d use Ember probably which gives you more with the class-oriented API. Without the simplified control flow of hooks, I don’t see what React even offers over Ember or Vue.
Could you say more about this? I'm not a React user, but to me one of the OO fundamentals is "object = behavior + state". What you're saying sounds so obviously correct to me that I guess there's something pretty weird going on in React-land?
Yes, there is something very weird indeed. Functional components are called every time they're rendered (that's not weird). But they have to maintain state between calls; they can't start over again fresh for each call/render (still not weird). So how do they do this? `const [state, setState] = useState(initialValue)`. You might look at that and think, I see useState being called, so it must be called on each render, so state is still not being preserved between calls.
But here's the weirdness. useState knows whether it's already been called for a given component; this is how it tracks state. The first call returns the state (the initial value) and a setter. Subsequent calls -- occurring after the first render -- do not return these objects anew, but instead reach into a per-functional-component store and return the values that already exist there. Calling the setter doesn't set the state in the body; rather, it updates the value in the store, then triggers a re-render.
If you're wondering just how react knows how to match up calls to useState between renders, it doesn't really. It simply matches them up based on the order they're called in. For that reason, you have to always call exactly the same useState calls on each render -- no putting some in an if statement or a variable length for loop, otherwise they'll be out of sync.
There are a bunch of other "hooks" for use in functional components, but they all work basically the same way: place some data in the functional component's store, then set up re-render (which, again, just means calling the component's function again) to occur when certain data changes.
It is bonkers. My number of "accidental" re renders on react are greater than before hooks.
Before at least I knew what was called on boostrap moments. Now everything is run as a side effect when any dependency change. My needs on checking if a prop or a dependency has changed hasn't evolved at all. I just have the core of my side effects on useEffect.
It sounds bonkers, but one of the reasons I pushed for moving my company’s app to React was because the useState hook reminded me of Reagent’s ratoms. I believe that was at least part of their intent. The catch is that you can’t really enforce immutability or atomic updates in plain JS, so they built hooks into the library as a workaround.
There’s not much of a cognitive difference between “defining a reagent component which uses ratoms and calls a pure function that derefs, updates or swaps them”, and “defining a react functional component that uses hooks”. It looks weird to have the “atoms” inside the render function instead of outside, and there are the aforementioned limitations (“the rules of hooks”), but it’s a compromise to get the feature into JS React in a consistent, performant manner.
I’d love to use CLJS/Reagent or even go full re-frame on our front-end codebase, but that’s a hard sell at my company since I’m the only dev who’s ever played with Clojure.
It's sort of what happens when OOP is demonized for years and FP is lionized. They've created what are effectively classes but with implicitly auto-generated private property names and a lot of weird edge cases where things can go pear shaped. But hey, it's functional and 'new' so it's good, right?
In fairness, the JS world has never had well engineered OOP GUI toolkits like you find in the desktop space. If you've never used JavaFX then ReactJS probably seems pretty magical. If you have, then, well ...
I know, but you didn't actually say you think React is awesome when not using JS. Given how JS specific React is, that's not a very intuitive outcome.
The comment was in response to "that's bonkers". And that has been my reaction on learning stuff like React and derived frameworks. A lot of it looks like functions for the sake of it, when objects already solve those problems but became unfashionable.
React added "hooks" which are basically methods and properties implemented as FIFO queues instead of lookup tables, with terrible syntax that requires you to declare them inside their constructor (the "functional" component's... well, function).
No, I'm not kidding.
Unless something's fundamentally changed about the code since release, they even end up attached to an object representing the UI component, by the time the heart of React's runtime code considers them. It's some real Rube-Goldberg shit. I read the code because I read the announcement docs and was like "wait, it looks like they... but no, surely they didn't" but yeah, turns out, they did.
What's worse, they have interesting "rules" that one really needs to use a linter so their IDE/Text editor gives them friendly reminders. One cannot conditionally call useEffect. One needs to add all dependencies to useEffect's dependency array - BUT that has potential to cause infinite re-renders (especially when using a getter/setter pattern with useState). They encourage DEFINING functions inside of other functions. Years of CS education and practice go right out the window because some popular JS person on Twitter says, "it's fine".
When pressed about it, be ready to be hit with, "You don't understand hooks".
I agree with the others. I've started using hooks since they were introduced and I've only hit the re-render issue only a few times, which I promptly fixed as they were caused by carelessness. My strategy is to not use a single useEffect, but multiple ones for each (which reduce greatly the dependencies). Also, avoiding dependency circles between useState and useEffect.
I've used class components and I think hooks are much better than the monstrosity that the lifecycle methods would usually become.
Also defining functions inside other functions is very much the staple of functional programming.
In React terms, "functional" almost always implies the opposite of purely functional. It just means that the component is declared by a function instead of a class. First time you call it, the function can assign state that it may reference in future calls, not unlike the methods of a class. How they actually go about this is however almost entirely weird.
Classes in JS are just functions anyway, and functions can have state. Saving the state of a function is a different thing, but of course JS allows that, too. Why is it weird?
Class instances are objects and it's the objects that have state. Functions can only abuse themselves as objects to store state.
function johnson() { johnson.state = {}; }
This is however shared state and the state will be reset whenever the function is called a second time. So what you normally do with a function is to pass the state via arguments and now the same function can work with different states. React could totally do this via props or as a second argument.
function Johnson(props, state) {}
Instead they changed the laws of functions so that a function can act different the first time it is called and also be called the first time multiple times if and when the function calls a functions that looks like JavaScript but follow different rules [1].
Functional components are great in that they are more terse, less boilerplate.
Class components just have more lines, more boilerplate, so they have a higher cognitive load. But when you have state, you have more complexity, you can't just wave your hand wave and make it go away. You need to take the complexity into account.
Hooks are trying to do hand waving. The thing is, once you get to non-trivial use cases, the handwaving stops working. You're better off thinking a lot about where you state lives in your component hierarchy, and then limit your state to where you really need it. Once you do that the overhead of class components doesn't really make that big a deal.
The same principle applies to almost everything in programming, the more thought you put into structure, the simpler you can make everything.
I think the problem is ES6 classes just aren't very flexible or expressive, compared to systems like Ruby, Smalltalk, or CLOS. For a lot of programmers, half-assed classes are worse than no classes, even when they can help with organizing state and behavior.
I'm definitely a novice when it comes to React, but I've shipped a beta version of an internal took using it. I never understood the emphasis on functional components. Everything I did using them was made more complicated and less obvious, especially for colleagues who are not familiar with React or JS in general.
React is built on components which are objects (both in a programming and GUI sense). Using OOP to describe them just makes so much more sense to me.
Functional components tend to be easier to compose. If your class component has very specific functionality that you won't need in other components it's fine but if you want functionality that will be shared across multiple components wrapping up that code into it's own hook is easier to share across components then trying to use wrappers, extending classes and HOCs.
Can you give me an example? I didn't have that experience. I extended classes for everything and it was super easy. But as I said, I was probably using react in a fairly basic way.
For example you have a button that you want to add a hover effect to. So it's got "onMouseEnter" and "onMouseLeave" handlers that sets a "hovered" state variable to true/false.
If you have that functionality in a class component, and want to share it with a new link component, then the React Class way is to create a "withHover" HOC and wrap your link component in it. withHover(LinkComponent) which will have the necessary variables and pass them as props to your LinkComponent which is "okay".
But what if you have like 5 of these little hooks? Your LinkComponent now needs to be wrapped in each (or them all wrapped into a single HOC) and has to accept the props for all of them. Or, if you're extending classes as you describe you now have class LinkComponent extends HoverClass, ClickClass, DataClass etc...
With functional components you have something like a "useHover" hook. It passes back the hover state and the "onMouseEnter" and "onMouseLeave" callbacks necessary for your link within the functional component itself. It doesn't have to expose props to it's HOC, so it's now self contained.
It seems the philosophy of react is to have a lot of "littler" discreet pieces of code be they components or hooks and not have to have a ton of dependencies between them. You tend to get better tests this way as well as it should make it easier for other devs as they don't have to know the whole system and can work on a more discreet bit.
I, personally, use function components pretty much exclusively these days. If the function gets too big with hooks I tend to just create a single hook from them, not because it's going to be shared but just to keep my code more manageable, the hooks create props and the components consume them.
To each their own. I don't seem to be frustrated by the problems many in this thread and the article share.
You aren’t the only person, I’ve certainly heard it before. But I’m yet to hear a convincing rebuttal to Mixins Considered Harmful [1], the hooks talk [2] or the Motivation section [3] on the announcement.
The arguments I’ve heard is that it is “cleaner”, “more understandable” or the OO style is preferred. Some developers feel uncomfortable with hook magic because it introduces a more niche programming concept than OO (e.g stateful functions). I don’t find these arguments compelling. What the React team managed to do was reproduce the same functionality as class components while decreasing the footguns associated with them.
I think there is more footgun in the hooks, especially rules of hooks. I worked with Junior dev and I can see unexperienced dev misused hooks more often than lifecycle.
I'm with you. I much prefer class components. So much easier to understand and work with.
I disliked hooks after first trying them on their release, and I hate them now.
I'm still bitter that a senior engineer didn't step in and prevent them from destroying the react ecosystem. I wonder if they were have been squashed if React was developed outside Facebook?
Well, unfortunately treating hooks like they're the worst thing since the devil is just the latest (and most humorous) in a long trend wherein a new syntax is added to something, and every developer thinks "okay, the preexisting alternative is now old and bad". I've been scolded many times in code reviews for absent-mindedly making a class-based component instead of using hooks (mind you, this was a legacy codebase FULL of class-based components. Are react developers just undereducated? Considering how many developers I've seen spend MONTHS "learning hooks" I think so. (Not to mention, every single React project I've been on in the last 7 years has been me and some developer who's never used React before, but has a lot of shallow blogpost-driven opinions).
It really rather seems like they achieved a near-perfect library for creating complex web applications, then in order to justify continued work on the project, simply kept making up unnecessary clever/cool features motivated by vibes that classes are passé.
I am in the same boat. I miss class components so much, but because of the trend it is hard to fight for it.
It is just so verbose and easier to read. It is more in line with the majority of graphics/ui frameworks in the whole software engineering ecosystem. It reminds me of render loop in many game engines, along with special event triggers (mount, unmount etc.). It makes me feel like an actual programmer, rather than a "view orchestrator"...
I used to be the same when I switched jobs and had to start doing functional. I now disagree that there are ever instances a class is needed. You can use hooks to encapsulate and reuse state, contexts, etc
Class component is still fully working as of latest React and won't be going away (at least according to the React docs). The main issue is when you work on a codebase that's full of function components and hooks it's hard to mix in class components.
me too use effect is too hard to track for anything but the trivial cases, class components are a good midway between being able to understand the program in 3 years and not having to manage one own state<>view mapping
Coming from Ruby and Python, I also prefer class components to hooks. I had to deal with hooks enough in Drupal/PHP which is in the process of deprecating them in favor of Symfony classes.
Drupal hooks are a functional approach to design compared to the OOP approach of Symfony components. As in you use functions to modify/extend existing code instead of inheritance. That's the context of the discussion, not whether they are otherwise similar to React hooks. Anyway, I just tend to think more in terms of making things with classes/objects than with functions.
Sure, it's often overkill and a functional component does the same thing with less code. Use a functional component in these cases. But if you're doing something more complicated then stop treating class components like the fucking devil. They have their place.