I've been in and out of the React world for the last 5 years, and this statement hit hard for me. Between the major shifts in best practices, the abstractions on top abstractions and constantly tripping up on the slight syntax differences between JSX and markup, so many commercial React apps I've worked on make me want to pull my hair out, and not just because of hooks.
In hindsight, the virtual DOM hype has not lived up to expectations, and I find newer frameworks like Svelte to be so much easier to work with. With the amount of React code running today, it's hard to see a future without it, but I'm so ready for it to be supplanted by something simpler.
And that's while writing a lot more React than I did Vue.
I haven't built anything with SolidJS, but it's not my style based on the docs. It is interesting that you bemoan being so far from "regular" code in Svelte when it's basically a minimal superset of HTML, as opposed to Solid, which uses JSX (which feels very far from "regular code" to me). Also given the fact that Svelte is a compiler that emits vanilla JS, I have a lot of more control over performance, compile-time checks for things like a11y, unused deps, etc. I'd argue that svelte source code looks a whole lot more like it's output than SolidJS does, but that't just me.
It's easily the best overall front end developer experience I've seen, but I've only built smaller projects with it so far.
I think this is true. However, I don't think I saw less bugs with `component(Did|Will)(Mount|Update|ReceiveProps)`. Lifecycle events are intrinsically about state management, and that has always been the root cause of many bugs. This isn't a React specific problem either. Back in AngularJS 1.2.x days, the $scope and digest cycle was the source of many bugs. In Backbone, people's two-way binding between views and models were the source of many bugs.
Are React hooks complex? Yes. I don't think they're worse than what existed before though.
With hooks, we traded verbosity for a single interface that does it all (assuming you know how to hook up your dependencies correctly, or compose helper hooks to manage state comparisons). Hooks allow you to do mostly anything lifecycle methods did, but they're a lot trickier to reason with, review, and develop.
This all goes away if all your developers are functional maestros - in practice, it's lead to buggier code across our various frontends.
People would also constantly get tripped up over `this.props` and `this.state` when it came to computed state values. Now a simple `useMemo` simplifies and expresses that intent way better than setting something conditionally in the constructor and doing a bunch of conditional checks on componentWillUpdate before calling this.setState again.
Edit:
Oh, and the improvements with Redux are life-changing. The `useTypedSelector` UX is so much better than writing a mapDispatchToProps, mapStateToProps, and then having a bunch of merging ceremony there.
So if you’re working on multiple tiny changing projects, or with contractors who are only gonna be around for 6-12 months, it’s possibly not worth it.
But if you’re working on a project that needs to stick around, and the people you’re working with are colleagues who will remain in the company even if not on the same project, the training is totally worth it, IMO.
And yes, the fact that the hooks work so much better with TS than `connect` was one of the major reasons why we now recommend the hooks API as the default.
I’m curious if the author sees dozens of problems every week in code review due to, say, unexpected null/undefined values, mistyping variable or attribute names, using the wrong number of equal signs, failing to catch errors and handle rejected promises, etc.
unexpected null/undefined values
This is an issue and has led to bugs. Switching to TS and making the compiler flag them fixed it.
mistyping variable or attribute names
Not an issue. Code obviously breaks if you have incorrect names.
using the wrong number of equal signs
This is an issue but causes few bugs. Linting generally catches it, though cute boolean punning still bites us.
failing to catch errors and handle rejected promises, etc.
This is an issue and has led to bugs.
Pretty much anything where there's some implicit details that the compiler or linters can't reason about programmers find a way to get wrong. One thing I like about the hooks linter setup is that what it encourages you to do by default will prevent most bugs, only lead to potential performance issues, unnecessary rerenders, unnecessary refetches.
> This is an issue and has led to bugs.
> Pretty much anything where there's some implicit details that the compiler or linters can't reason about programmers find a way to get wrong. One thing I like about the hooks linter setup is that what it encourages you to do by default will prevent most bugs, only lead to potential performance issues, unnecessary rerenders, unnecessary refetches.
This is something that can be mitigated via the no-floating-promises linter rule if you're using TypeScript[0]. For the cases where you actually want to just swallow errors, you can just add a `.catch(noop)`. This makes such situations explicit. You can get even stricter with the no-misued-promises rule[1].
[0]: https://github.com/typescript-eslint/typescript-eslint/blob/...
[1]: https://github.com/typescript-eslint/typescript-eslint/blob/...
touching on an immortal truth!
Most common I see:
1: Missing dependencies (for folks who don't use the linting rule)
2: Not understanding reacts async state model: example being expecting state to have been updated immediately after calling setState inside an effect
3: Not understanding closures: an example is creating an interval in an effect that uses and updates a useState value, but being confused why it isn't updated when the interval repeatedly fires.
I agree wholeheartedly with the author. Hooks are powerful and even I was super excited to start using them when they released. But now I think the hooks paradigm leads to even worse and bug-ridden code than what we had before.
The docs are pretty good, but don't seem to cover very well that when using hooks you really need to know JS well, which includes equality in JS, closures, etc otherwise you will be guaranteed to shoot yourself in the foot.
There is so much more business logic mixed in with component code at seemingly random in projects as well such that it makes finding where "stuff" happens even more difficult than before.
> I do not exaggerate when I claim that I find a dozen of hooks-related problems every single week while reviewing code.
I also see sooo many issues when reviewing code like leaked event listeners, unstable props causing re-renders, etc. And these issues show up from teammates who otherwise write impeccable and trustworthy PRs in other regards.
I enjoy writing hooks style code, and for me reasoning about lexical scope & closures is second nature. But for many engineers used to OOP, hooks code is the first time they’re asked to do this kind of reasoning since leaving university. In OO Java/JavaScript, it’s very normal to declare a new class and have the only two scopes be the current function’s locals, and class instance members. Hooks code on the other hand can easily reach two or three layers of local closure scopes, each with different lifetimes. I think this is fun and clever, but I also prefer to maintain boring and simple code… I’m worried that hooks ramps up trivial complexity too much in exchange for often-unneeded power.
On the other hand, function components and hooks tend to guide people more toward splitting up their big mega components into smaller parts. At all companies I’ve worked for, product eng across the stack tends to produce mega objects cause it’s easy to just keep adding private methods to do one more thing, and splitting up responsibility of state encapsulation takes some extra reasoning. At least with FC/hooks, the API and linter force you to unroll funky logic or loops into sub-components, since you can’t call hook in loop or conditional.