I absolutely love hooks and don't want to use the `React.Component` class ever again.
And yes, hooks need to be grasped, they're quite something else. But once you get the hang of hooks, they're very simple to understand.
OP seems to be a bit stuck in a hole.
If you end up in a situation where your hooks come loose and the code becomes a mess — just delete them all and rethink your code / component logic structure. Believe me, it all can be simplified. Just rinse and repeat until you are satisfied with the outcome. That's when the "aha!" moment comes.
At the moment I only use `useEffect`, `useState`, `useRef` — the rest is unneeded so far even in my large-ish application.
There are some weird edge cases with useEffect, no question.
I don’t think they’re bad enough to want to go back to using classes... but I think the OPs post was a bit more specific than you’re giving them credit for, and they stuff like useAsyncEffect exists because it is a common pain point.
The behaviour is weird sometimes; just because you can use the trivial use case easily doesn’t mean you won’t stumble, and if you do, good luck trying to understand what went wrong.
> ...they're very simple to understand...
They’re not simple.
They just superficially appear simple, which is good enough most of the time.
I think it’s entirely fair to complain that’s not ideal... even if you still think there’s a major benefit to the trade off compared to classes (which I do think is true too).
...but I’m very sympathetic to people hitting edge cases.
On this particular point, the reasoning is that refs are meant for values that don't need to trigger a rerender[1] -- an escape hatch, rather than something you reach for by default.
In that sandbox, you can accomplish what you mean to accomplish by using state rather than a ref.
I suspect this may be one of those cases where there's an 'aha' moment involved, and people who get that moment early find it simple to understand, people who haven't got to that moment yet are going argh, and people who got to it late sigh and/or write "yet another X tutorial" in the hopes that how they got their aha moment will work for other people.
The classic X here, of course, is monads.
But as personal examples, it took me ages of headbanging to get the 'aha' moment for git, even though a bunch of people I know got it almost immediately.
In the case of hooks, once I built a mental model of how they were (theoretically) implemented, I went 'ooh' and found them obvious and relatively easy from then on. It looks to me like the author of this piece didn't have that luck, and so for him, they're not simple (although maybe later, post-aha-moment, they will be).
tl;dr: "They seem simple to understand once you understand them, but are mystifying up until that point" seems to me to be the most likely hypothesis here.
I think you can make that first useEffect block fire by passing in an empty array as your params. This should run the code block on mount. To run a function on unmount, return it from the same useEffect with the empty array params.
Have you seen the ReactConf talk on hooks? [1] It's absolutely worth the watch, even if you're somewhat familiar with hooks. They go through the pitfalls of class-based components and how hooks solve them.
It more-or-less boils down to: 1. class-based components force your lifecycle logic to live in disparate locations. 2. Class-based hooks are obtuse with hidden gotchas whereas pure functions tell you exactly what they're doing. 3. A whole class of unergenomic solutions disappears when you use hooks.
It's not classes themselves, it's the lifecycle methods, the constructor, this, state, and it all interacts. Hooks let you do more or less the same thing, but IMHO with a simpler and clearer API.
You have more control too. Look at the parameters to useState to see what I mean.
> If you end up in a situation where your hooks come loose and the code becomes a mess — just delete them all and rethink your code / component logic structure
“Just burn it to the ground and start over,” isn’t an inspiring endorsement of the methodology...
`useContext` is excellent. React.context is a solid alternative to the likes of Redux and Apollo, but takes a fair amount of boilerplate to add to a class based React component. But with `useContext`, applying global state is a breeze. A lot easier to manage than Redux, at the very least.
The only real dislike I have, is in how `useEffect` confuses the React lifecycle. It took me a little while to grok the lifecycle idea, but it was/is a useful way to conceptualise what happens to your components at runtime.
But now, you have to remap all that knowledge to the hooks workflow. It's not a huge challenge, but it's yet another overhead.
Indeed, starting out, our team ran into a number of issues with infinite rendering loops and the like. Took a fair amount of reading to discover where the pitfalls were.
I understand how contexts can replace Redux, but can you please elaborate how they can be used instead of Apollo? Do you mean the whole of Apollo or Apollo local state?
We're slowly refactoring our class-heavy React app to functional components with hooks.
One thing we haven't been able to replace, though, are classes that depend on refs. How to do this, since there's no instance with functions like there are with classes?
I was extremely skeptical of hooks in the beginning after having used class-based lifecycle components for a long time. Overall, I _think_ it's been a net positive but I end up feeling that I'm writing more code at the expense of using well defined constructs (lifecycle methods).
I usually feel like I'm writing too many effects which update state(s) and the lifecycle flow becomes harder to contain mentally, however there are times where I'm like "this is definitely easier or less of a cluster then it use to be".
So I think the jury is still out. In terms of Hook's initial premise of "we're doing this so that developers unfamiliar with class-based OO paradigms can work better/faster", I don't think it added any more clarity or ease-of-use over the class based lifecycle methods tbh.
Maybe this is because I'm a "developer[] unfamiliar with class-based OO paradigms" but I find hooks components easier to read later. You start at the top, read to the bottom, and assuming you can catch crazy indirection within a hook callback during review, that's what happened. No HOCs. No having to memorize an externally documented lifecycle order. No having to cross-reference methods against the state they update 50-60 lines up the file.
I'm genuinely curious if the difference is because I was doing tons of Recompose/HOC style components before hooks came out.
Also, just FYI, it didn't click for me until this tweet[1]:
> The question is not "when does this effect run" the question is "with which state does this effect synchronize with"
Many people aren't event aware of the unnecessary renders caused by using hooks. If your app is so tiny that it doesn't matter if everything renders all the time, then you might not be aware of your `useCallback` recreating callbacks way too often (https://github.com/facebook/react/issues/14099), or that you're not even using it in the first place. Considering that front end is probably the area of software development that attracts the largest share of people like me - self taught, no computer science background - I'd also assume that many people love the perceived simplicity of hooks without realizing that it makes their code worse.
It'll only become apparent when you app is large and complex enough that it suddenly starts to matter when things re-render and then you'll be faced with tracking down those re-renders and fixing dependency arrays everywhere.
With classes, the naive solution is the right one when it comes to preserving function identities across renders.
Not saying hooks are bad, but they're not all roses and sushine.
A lot of people don't understand that React's default behavior is to re-render _everything_.
When a component is rendered, React will recursively re-render all descendants of that component. Out of the box, React doesn't do any optimizations like "skip rendering this component if the props haven't changed". Because of that, "re-creating callbacks" isn't an issue in base behavior [0], because there's nothing that cares if it's a new function reference or not.
It's only when child components are attempting to optimize perf by comparing props and avoiding re-renders that having consistent callback function references matters (ie, `React.memo()`, `PureComponent`, and React-Redux's `connect()`).
Yeah, React seems to have been designed under the assumption that DOM operations are the only thing front-end code can do that takes non-zero time. Like, here's the first line of their document describing how to use to improve performance using things like PureComponent: (https://reactjs.org/docs/optimizing-performance.html)
> Internally, React uses several clever techniques to minimize the number of costly DOM operations required to update the UI. For many applications, using React will lead to a fast user interface without doing much work to specifically optimize for performance.
My experience with React applications (both writing them and as an end-user) hasn't really borne this out.
> re-renders that having consistent callback function references matters
That's the case I was talking about, I probably should have added that. In our codebase at work most components are using such optimizations but of course that's not the case for most people.
I have been working on an enterprise React App for the past 6 months using only the hooks API. The App is a non trivial Dashboard, displays various custom charts (done using d3 and manual SVG Elements in React) and some larger tables/forms. While I use useEffect() frequently, there is not a single useCallback/useMemo in the code.
I just profiled it on my 5 year old MacbookPro, and out of 50 commits or so, just a single one spiked above 20ms (which was the first commit, subsequent to same view were much faster) and almost all are in the <5ms range. I decided it is not worth doing any optimization yet, and I will defer useCallback/useMemo optimization until there is a real, noticeable delay in rendering.
> This is yet another JavaScript paradigm to learn. For the record, I am a 49-year-old React fanboy. I am a freelancer and use other frameworks apart from React, and this gives me fatigue.
Amen to that. The fact that still are in "here's a 'better' idea, let's try this" landscape in JavaScript is depressing.
I no longer jump on these new frameworks when the bandwagon flies by. I ignore postings for jobs saying they are rewriting their system in "new Framework Y!". I don't care how "pure" your new design is, I care about having it work well in the trenches.
React Hooks and Containerizing Everything are the things this old "Webmaster" is kicking down the road until all the bugs are shaken out. I also skipped gulp and grunt and jumped straight into webpack, which I've read far too much about to understand so little.
Interesting way of thinking - hooks _are_ a new framework, they are just marketing it under the same name.
I guess I'm old too. When someone shows me what (real) pain hooks solve on non-Facebook-size codebases, I'll be all ears. Until then... Thank you, classes work just fine and make my code nice and readable.
Overall I think React hooks are an improvement. My codebase is usually shorter and there is a lot less typing involved.
But hooks creates abstractions the developer needs to deal with that never existed before.
Hooks are not functional because they break referential transparency of functions.
You have to track dependencies manually and hooks are more difficult than they need to be for the "componentDidMount" equivalent. If you don't get the dependencies just right you end up with things not firing or in infinite loops.
You have to wrap your functions in "useCallback" or "useRef" just so the reference doesn't change and cause infinite loops.
You can't create abstractions where a hook calls another hook. So you end up having to inline a bunch more code into your function rather than outsourcing it into a helper function.
The positional order seems like it would be easy to work around if they allowed you to pass in a key. Not sure why that isn't available.
> The positional order seems like it would be easy to work around if they allowed you to pass in a key. Not sure why that isn't available.
IIRC it's because they implemented their own method lookup table (!) to associate with the Component object (!) but as a FIFO queue, more or less. I assume either for ideological (that's how they wanted it to work) reasons or because they (probably correctly) reasoned that loading down React apps with more strings at such a basic level would risk performance/memory problems. Plus if they did that then it'd really look like a method lookup table and be more obviously Rube-Goldbergian than it already is.
The primary concern was transparent composition and enabling "custom hooks". All of the "named/keyed hooks" proposals from the community failed that test.
Custom hooks naturally fall out of the current implementation. Technically, React doesn't even know that custom hooks exist - it just knows that more of the primitive hooks are being called inside your own component.
If you've got time, skimming the original React Hooks RFC https://github.com/reactjs/rfcs/pull/68 ) is informative (and admittedly difficult, because there's hundreds of similar comments).
They can make the string part optional. Variables and symbol tables have been staples of programming languages and compilers for ages now. It's a very standard pattern. Nothing Rube-Goldberg about it.
But React has deviated so far from both the OOP, FP, and traditional programming paradigms that now it kind of feels like hacks are needed to compensate for hacks.
I never understood the need for hooks and how it's used (the example that react doc has for useState is too simplistic for any useful pedagogical reasons) until I saw divjoy's generated code and studied how it used hooks. Then everything clicked and I've been more productive using hooks than I ever had with react classes. I can attest that cognitive load is lower with hooks than with classes.
I don't agree with the author's statement that there are only two (very involved) solutions to the pagination problem
(re: section titled "React relies on the order in which Hooks are called").
It can easily be solved by just setting the state in the event handler, since the state is included in the dependencies array for the fetch effect:
> On line 23, the useFetch Hook will be called once on the first render. On lines 35 – 38, pagination buttons are rendered but how would we call the useFetch Hook from the event handlers of these buttons?
We can call `useFetch` again by triggering a re-render. This hasn't changed at all with the introduction of hooks: new props or new state still triggers a re-render.
I wondered about this too, in general I think the article misses that updating state is the key to "causing" effects. Rather than latching onto the callback model to explicitly "run" effects.
Though I'll admit getting the relationship of state and effects caused by changes to that state can be more difficult to work out mentally, I think it results in a more complete understanding of what a component actually does.
I spent maybe 1.5-2 years working on a medium size React app and after taking maybe 6 months off, I came back to it. I started reading about React Hooks and honestly I did not fully understand them. Maybe it's just JS fatigue and me feeling like I'm trying to building a house on quick sand. Everything is always changing...
Recently I built a bolierplate JSON-api web app project in vuejs and golang (previously I was using TypeScript, Apollo GraphQL and Express). I find the terminology around vuejs much more user friendly and it comes with a router and state management built in...I try to avoid using any other JS module. The golang ecosystem is much more stable than nodejs which is a relief....I have a feeling I might just settle on vuejs + golang for all future web apps
And yes, hooks need to be grasped, they're quite something else. But once you get the hang of hooks, they're very simple to understand.
OP seems to be a bit stuck in a hole.
If you end up in a situation where your hooks come loose and the code becomes a mess — just delete them all and rethink your code / component logic structure. Believe me, it all can be simplified. Just rinse and repeat until you are satisfied with the outcome. That's when the "aha!" moment comes.
At the moment I only use `useEffect`, `useState`, `useRef` — the rest is unneeded so far even in my large-ish application.
P.S. sorry for the puns!
There are some weird edge cases with useEffect, no question.
I don’t think they’re bad enough to want to go back to using classes... but I think the OPs post was a bit more specific than you’re giving them credit for, and they stuff like useAsyncEffect exists because it is a common pain point.
For example, this useRef / useEffect combo mystifies me even now: https://stackblitz.com/edit/react-ts-zhvuha
The behaviour is weird sometimes; just because you can use the trivial use case easily doesn’t mean you won’t stumble, and if you do, good luck trying to understand what went wrong.
> ...they're very simple to understand...
They’re not simple.
They just superficially appear simple, which is good enough most of the time.
I think it’s entirely fair to complain that’s not ideal... even if you still think there’s a major benefit to the trade off compared to classes (which I do think is true too).
...but I’m very sympathetic to people hitting edge cases.
On this particular point, the reasoning is that refs are meant for values that don't need to trigger a rerender[1] -- an escape hatch, rather than something you reach for by default.
In that sandbox, you can accomplish what you mean to accomplish by using state rather than a ref.
[1] https://github.com/facebook/react/issues/14387#issuecomment-...
The classic X here, of course, is monads.
But as personal examples, it took me ages of headbanging to get the 'aha' moment for git, even though a bunch of people I know got it almost immediately.
In the case of hooks, once I built a mental model of how they were (theoretically) implemented, I went 'ooh' and found them obvious and relatively easy from then on. It looks to me like the author of this piece didn't have that luck, and so for him, they're not simple (although maybe later, post-aha-moment, they will be).
tl;dr: "They seem simple to understand once you understand them, but are mystifying up until that point" seems to me to be the most likely hypothesis here.
It more-or-less boils down to: 1. class-based components force your lifecycle logic to live in disparate locations. 2. Class-based hooks are obtuse with hidden gotchas whereas pure functions tell you exactly what they're doing. 3. A whole class of unergenomic solutions disappears when you use hooks.
[1] https://www.youtube.com/watch?v=dpw9EHDh2bM
You have more control too. Look at the parameters to useState to see what I mean.
* Removed "pure" to make my main point clear.
“Just burn it to the ground and start over,” isn’t an inspiring endorsement of the methodology...
The only real dislike I have, is in how `useEffect` confuses the React lifecycle. It took me a little while to grok the lifecycle idea, but it was/is a useful way to conceptualise what happens to your components at runtime.
But now, you have to remap all that knowledge to the hooks workflow. It's not a huge challenge, but it's yet another overhead.
Indeed, starting out, our team ran into a number of issues with infinite rendering loops and the like. Took a fair amount of reading to discover where the pitfalls were.
One thing we haven't been able to replace, though, are classes that depend on refs. How to do this, since there's no instance with functions like there are with classes?
https://reactjs.org/docs/hooks-reference.html#useimperativeh...
If you ran into something that isn't quite covered by this hook, I'm also curious! Can you write a minimal example in codesandbox and share?
I usually feel like I'm writing too many effects which update state(s) and the lifecycle flow becomes harder to contain mentally, however there are times where I'm like "this is definitely easier or less of a cluster then it use to be".
So I think the jury is still out. In terms of Hook's initial premise of "we're doing this so that developers unfamiliar with class-based OO paradigms can work better/faster", I don't think it added any more clarity or ease-of-use over the class based lifecycle methods tbh.
I'm genuinely curious if the difference is because I was doing tons of Recompose/HOC style components before hooks came out.
Also, just FYI, it didn't click for me until this tweet[1]:
> The question is not "when does this effect run" the question is "with which state does this effect synchronize with"
> useEffect(fn) // all state
> useEffect(fn, []) // no state
> useEffect(fn, [these, states])
[1] https://mobile.twitter.com/ryanflorence/status/1125041041063...
It'll only become apparent when you app is large and complex enough that it suddenly starts to matter when things re-render and then you'll be faced with tracking down those re-renders and fixing dependency arrays everywhere.
With classes, the naive solution is the right one when it comes to preserving function identities across renders.
Not saying hooks are bad, but they're not all roses and sushine.
When a component is rendered, React will recursively re-render all descendants of that component. Out of the box, React doesn't do any optimizations like "skip rendering this component if the props haven't changed". Because of that, "re-creating callbacks" isn't an issue in base behavior [0], because there's nothing that cares if it's a new function reference or not.
It's only when child components are attempting to optimize perf by comparing props and avoiding re-renders that having consistent callback function references matters (ie, `React.memo()`, `PureComponent`, and React-Redux's `connect()`).
[0] https://reactjs.org/docs/hooks-faq.html#are-hooks-slow-becau...
> Internally, React uses several clever techniques to minimize the number of costly DOM operations required to update the UI. For many applications, using React will lead to a fast user interface without doing much work to specifically optimize for performance.
My experience with React applications (both writing them and as an end-user) hasn't really borne this out.
That's the case I was talking about, I probably should have added that. In our codebase at work most components are using such optimizations but of course that's not the case for most people.
I just profiled it on my 5 year old MacbookPro, and out of 50 commits or so, just a single one spiked above 20ms (which was the first commit, subsequent to same view were much faster) and almost all are in the <5ms range. I decided it is not worth doing any optimization yet, and I will defer useCallback/useMemo optimization until there is a real, noticeable delay in rendering.
Amen to that. The fact that still are in "here's a 'better' idea, let's try this" landscape in JavaScript is depressing.
I no longer jump on these new frameworks when the bandwagon flies by. I ignore postings for jobs saying they are rewriting their system in "new Framework Y!". I don't care how "pure" your new design is, I care about having it work well in the trenches.
I, too, am tired.
And old.
I guess I'm old too. When someone shows me what (real) pain hooks solve on non-Facebook-size codebases, I'll be all ears. Until then... Thank you, classes work just fine and make my code nice and readable.
But hooks creates abstractions the developer needs to deal with that never existed before.
Hooks are not functional because they break referential transparency of functions.
You have to track dependencies manually and hooks are more difficult than they need to be for the "componentDidMount" equivalent. If you don't get the dependencies just right you end up with things not firing or in infinite loops.
You have to wrap your functions in "useCallback" or "useRef" just so the reference doesn't change and cause infinite loops.
You can't create abstractions where a hook calls another hook. So you end up having to inline a bunch more code into your function rather than outsourcing it into a helper function.
The positional order seems like it would be easy to work around if they allowed you to pass in a key. Not sure why that isn't available.
Not sure if I understand you correctly, but isn't this the purpose of custom hooks? You should be able to freely call hooks within other hooks.
Of course you need to follow the same rules in the hook (always call every hook it calls, in the same order, so you don't mess up the order above).
IIRC it's because they implemented their own method lookup table (!) to associate with the Component object (!) but as a FIFO queue, more or less. I assume either for ideological (that's how they wanted it to work) reasons or because they (probably correctly) reasoned that loading down React apps with more strings at such a basic level would risk performance/memory problems. Plus if they did that then it'd really look like a method lookup table and be more obviously Rube-Goldbergian than it already is.
Custom hooks naturally fall out of the current implementation. Technically, React doesn't even know that custom hooks exist - it just knows that more of the primitive hooks are being called inside your own component.
If you've got time, skimming the original React Hooks RFC https://github.com/reactjs/rfcs/pull/68 ) is informative (and admittedly difficult, because there's hundreds of similar comments).
But React has deviated so far from both the OOP, FP, and traditional programming paradigms that now it kind of feels like hacks are needed to compensate for hacks.
It can easily be solved by just setting the state in the event handler, since the state is included in the dependencies array for the fetch effect:
https://codesandbox.io/s/dependency-array-0u3sc
The answer to this question posed by the author?
> On line 23, the useFetch Hook will be called once on the first render. On lines 35 – 38, pagination buttons are rendered but how would we call the useFetch Hook from the event handlers of these buttons?
We can call `useFetch` again by triggering a re-render. This hasn't changed at all with the introduction of hooks: new props or new state still triggers a re-render.
Though I'll admit getting the relationship of state and effects caused by changes to that state can be more difficult to work out mentally, I think it results in a more complete understanding of what a component actually does.
A good mental model is that the second argument to useEffect defines which pieces of state that effect syncs with.
useEffect(() => {}, undefined) // Sync with all state
useEffect(() => {}, []) // Sync with no state
useEffect(() => {}, [a, b]) // Sync with `a` & `b`
Recently I built a bolierplate JSON-api web app project in vuejs and golang (previously I was using TypeScript, Apollo GraphQL and Express). I find the terminology around vuejs much more user friendly and it comes with a router and state management built in...I try to avoid using any other JS module. The golang ecosystem is much more stable than nodejs which is a relief....I have a feeling I might just settle on vuejs + golang for all future web apps