What's hard about React Hooks for you?

kentcdodds profile image Kent C. Dodds ・1 min read

As you've been using/learning about React Hooks, what have been the hardest parts for you? #discuss


markdown guide

They work like black magic.

It's actually kind of clear why they behave like they do (they're called in sequence), but the consequences in terms of code organization and structure are... meaningful, at least, if not outright jarring.

You can't have a hook called inside an if branch, for start. That's enough to be a game changer. But then, the usual lifecycle hooks like componentDidMount and so on are all condensed inside useEffect with its special second argument logic.

With classes we had a clear idea of the lifecycle of a component. With hooks it's distributed inside a lot of use* functions with often not really meaningful names.

Not that the names chosen by the React team are clear either, mind you. useCallback, useReducer, useLayoutEffect and so on aren't IMO clear at all about their intent and usage. Compare those names with the aforementioned componentDidMount.

I've found myself read the documentation about them more times than I'd have liked to. And I think I'll do that again on the next project 😔

All in all, I think hooks are a clever and simple solution to most of the reactive application problems, but they're a shifting paradigm - not that it's unsurmountable but enough to make "classic" React something completely alien. At this point, would it be so bad to fork React into a hooks-only separate library?


At this point, would it be so bad to fork React into a hooks-only separate library?

I agree with this, but I feel that its way too late now. Also is the intention to eventually fade out "classic" react?


I agree with this, but I feel that its way too late now.

Interesting statement. Hooks became stable on Fabruary 6th with React 16.8, not even 6 months ago. When do you think it was the appropriate time? Maybe before so many libraries using hooks popped out like mushrooms after the rain? 😅

Also is the intention to eventually fade out "classic" react?

They (React maintainers) said there's no intention to dismiss class components in the foreseeable future. But what about the hidden intentions? In the end, the explicit goal was to push developers away from class components.

Was release only ~6 months ago? I started playing with hooks before full release though, so feels longer to me I guess...

I feel like the right time should have been when I was announced and was known to be so dramatically different. Have it as a React add-on. I imagine that there would be way less usage of it if that was the case though.

Idk really, maybe the ideal solution would be to go the babel route? @React/core with optional @React/classes and @React/hooks or something.

I expect that classes will eventually be extracted to a separate package and a codemod will be made available to update code to use that package, similar to what was done with React.createClass. The react team is very interested in making it easier to build UIs with React and they feel that hooks is the way to do this. So putting hooks in a separate package wouldn't have pushed their goals forward very well. I'm happy with the way it's been done so far. It's a change, and that comes with a learning curve, but it's one that I'm glad to have for the benefits that hooks provide.

That's an interesting insight, Kent. Thank you!


If I have to consider something, give me a hint of what it's about 😄

Anyway, interesting video, very clear. Shawn is excellent at explaining stuff.

But, IMO, the point still remain. A developer could learn those things, but they shouldn't be forced to do so. They're glimpses of the internals of hooks. But, as Shawn says at the end of the video, that's not React - so it's unclear how, for example, removing a component from the tree affects the hooks array. Or why you can't use hooks inside class components. Or why there's a useEffect hook but also a useLayoutEffect and how - or why! - they differ.

This is complicated by the fact that function components do have a lifecycle just like class components, but it's hidden under the rug of hooks.

I personally think it’s only complicated because we’re so steeped in OOP and lifecycle methods this seems so alien.

Yes, that indeed might be the case.

I also wonder why, at this point, it is the case. I.e., why a lifecycle seems so natural at this point. It's not like someone imposed this concept on everyone - maybe it's just because we usually see something "coming to life", "living" and "dying" that we apply this idea even to application components 😬

Also, React indeed has a "cycle" to make things work - the work loop. Which works well with JavaScript's event loop too.

In the end, I'm unsure if there are solid, evident advantages about hooks that makes us say that they're definitely the better way to create (React) components. They comes with compromises, like basically everything else, and I'm unconvinced that the drawbacks could be nullified by mere habit.

But for now, I still think it's too soon to declare something.

My feeling at the moment is that Hooks is an attempt to solve issues they created themselves by pushing functional programming too far (HOC wrapper hell, etc.).
That's weird because it's pretty easy and elegant to avoid those problems if you use the right OOP patterns (adapters, dependency injection...).
I wish they had improved the class usage rather than introducing those terrible, anti-pattern, useThings.
They are shiny and new, they are nice for small components and todo demo apps, but for large apps, the problems remain.

That's an interesting take as they're using closures. Which as you may know isn't new, nor specifically "functional".

HOC wrapper hell is also interesting as in my experience most HOCs issues are down to bad architecture and exhibit similar as inheritance.

"They are shiny and new" - they're old. Only new in React land.


To add to this already great comment, hooks have felt more like a fad, not only in the way it was introduced but also the way it was adopted. For example, hooks was released partly to help with newer developers that don't understand classes. Some of my primary concerns are that the JavaScript community and ES standards have been heading in the direction of more OOP with ES6 -> ESNext. Hooks are a fork essentially moving away from that and more down the functional route. I agree there are times and places for hooks, but I feel they can be overused.

For instance, when you come across a functional component that has 6 different useEffect calls, the function ends up being 1k lines long, which isn't manageable. The same goes for finding a class componentWillReceiveProps that is super long too, the tool is only as good as the person using it.


One of the most common struggles I've seen people have is not being able to remove the idea of imperative life cycles from their mental flow, and switching to thinking in terms of declarative effects.

It's really strongly ingrained in a lot of users to think more about mounting, updating, and unmounting instead of just rendering, and what (effect) you want to happen when you render, given some state.


I find this is the most unnatural part of hooks: the lifecycle paradigm fit the mental model of React/DOM interaction a lot better than hooks, and many integrations with non-React libraries require instantiating on mount and destroying on unmount.


It's been a common riff the last few days, so I've written a post on it: dev.to/samsch_org/effects-are-not-...

  • The semantics are terrible ( useState(false) 😱, useEffect() 🤔). It's not meaningful, and this is not a good sign. I've worked with many languages and I've never seen anything like that. We're not all React pundits / teachers. Many of us are fullstack devs having to deal with other languages, Kubernetes, etc and deliver real work, and with Hooks, more "Javascript fatigue" ensues.
  • They look quite simple in code examples, but they are not that much in real life. The complexity is just shifted to other places, and not necessarily in a good way. The Additional Hooks sections gives you a hint of the troubles.
  • I love functional programming, but I think they've been pushing it too far (HOC, I'm looking at you), and apparently they have no plan to calm down. I wonder where their hatred of classes comes from.
  • This way of pushing new features to the community is questionable. In the python community, anything like that would be discussed for months before being implemented in the language or framework.

I would love to know why the React team hates classes so much. It's a similar story with Vue, they're pushing anti-classes rhetoric when there is nothing wrong with classes. The arguments people have against them all mostly relate to inheritance, completing ignorant of the fact this is the reason we have decorators (for metaprogramming).

Angular and Aurelia devs have been building apps just fine with classes since 2015. It seems to me that React and now Vue is so against them because their designs predate ES classes and classes are a convenient scapegoat for outdated architecture. I have never seen a valid argument against classes in Javascript that didn't boil down to personal opinion or design differences.


References in custom hooks

For me, one the hardest thing with hooks is about following the references when creating custom hooks.

I end up with a lot of useMemo()s and the "not recommended" useEventCallback technique and a lot of ref tracing of make sure it's not my custom hook causing unnecessary renders.


const useRelatedState = (
  defaultMapToState = () => {},
  identifier = v => v
) => {
  const [map, setMap] = useState(() => new Map());

  const tuplesWithRelatedState = useMemo(
    () =>
      items.map(item => {
        const id = identifier(item);
        return [item, map.has(id) ? map.get(id) : defaultMapToState(item)];
    [items, map, defaultMapToState, identifier]

  const setStateForItem = useCallback(
    (item, newState) =>
      setMap(map => {
        const id = identifier(item);
        const currentState = map.has(id)
          ? map.get(id)
          : defaultMapToState(item);
        if (typeof newState === 'function') {
          newState = newState(currentState);
        if (currentState !== newState) {
          return new Map([...map.entries()].concat([[id, newState]]));
        return map;
    [setMap, identifier]

  const ref = useRef();
  ref.current = useCallback(
    mapRelatedState => {
      const newStates = items.map(item =>
        mapRelatedState(item, map.get(identifier(item)))
      if (
        items.length !== newStates.length ||
        items.some((item, i) => map.get(identifier(item)) !== newStates[i])
      ) {
        setMap(new Map(items.map((item, i) => [item, newStates[i]])));
    [items, map, setMap, identifier]
  const setAllState = useCallback(
    mapRelatedState => {

  return [tuplesWithRelatedState, setStateForItem, setAllState];

Async effect without use actions

Also, any async effects that change state but are not caused by a user action, such as loading a list of things when first rendering a component...

const AsyncDataList = () => {
  const [muppets = [], setMuppets] = useState();
  useEffect(() => {
  }, []);

  return (
      {muppets.map(muppet => {
        return <li key={muppet}>{muppet}</li>;

...makes the component annoying to test because it currently throws up a warning about not being wrapped in act and currently act() is a synchronous call

describe('Test a loading component', () => {

  test('async data loading', async () => {
    const { getByText } = render(<AsyncDataList />);
    await wait(() => getByText('Kermit'));
Warning: An update to AsyncDataList inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  * fire events that update state *
* assert on the output *

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act

There is going to be a "fix in the next version", about the asynchronous part, but will it help with async effects that have no user action to initiate them?

Reset state based on props

Also I often find myself wishing I could "reset the state" based on a prop change. The only easy work around I've found that seems to work is just dispatching/calling a callback in useEffect that run when the prop changes.

const MyComponent = ({ theProp }) => {
  const [someState, dispatch] = useReducer(reducer, { initialState: true ));

  // 😕best idea I've had so far
  useEffect(()=> dispatch({type: 'reset'}), [theProp]);

If you want to reset all the state, you can assign that prop the key of the component, and it will rebuild that component in it's original state


Right, yes thank you, I can push the problem up and reset with a key.

But you know, sometimes it’s not the only state, and it would be nice to solve the problem internally in the component rather than lift the problem up, but yes, great idea, thanks

Hmmm, interesting. My brain's not going to let this one go... Could you provide a concrete example? and I'll let you know what I come up with.

Okay @wolverineks , here's a derived but concrete example:

Let's say you've got a component that lists the members of a team, and you can switch the teams and view the different members in the members-list.

In that list, each team member has a small UI state, to determine if the details view is showing, this doesn't seem appropriate to add to the team-member itself, because that object is provided from elsewhere (global/domain state), and it is really just a UI concern.

In the list of members, there is also a toggle with the option to "Hide Inactive Members", so you may not want to use the "reset with a key" method when the team changes, because you'd lose the "Hide Inactive Members" state.

So in the example above, you can look at the custom hook use-open-state.js and see that what I am doing is using useEffect to re-set the state for "what's open" whenever the list changes.

There are lot's of ways to solve this particular problem, keep a member mapping in state no matter what team shows, some crazy memoization, moving the "hide inactive members" state out of the members list (and use key to reset instead)... lot's of ways, it's not an insurmountable problem.

But my point was, I often find myself wishing useState() (or useReducer()) would take an inputs/deps argument like useEffect/useMemo/useCallback, so I could just reset my initial state based on a prop.

Something like...

useState( list.map(addExtraProp), [list] )

I think that would be swell.

I think the simplest solution to this problem is to Lift State Up no?

Well, personally I think useState() having a method of reseting state would be the 'simplest' solution...

useState(list.map(addExtraProp), [list]);

...but yes, I could lift the state out of the MembersList, and convert it to a fully controlled stateless component, and just reset the state in the handler for the Team's dropdown onChange. Definitely another good way to solve the problem.

I guess maybe I am the only one who ever felt like they wanted a way to reset the state in useState() or useReducer()


I guess this post is kinda old - but it popped up today in my Dev.to feed, so...

On a practical level, I've noticed that the "life cycle" of Hooks isn't completely analogous to class-based components. This has caused me some degree of headaches, and I've only recently begun using Hooks heavily. They say things in the docs like "this Hook is analogous to this lifecycle method", but, umm... not exactly. I know that part of this is just about learning the ins-and-outs of Hooks, but it can still be frustrating when you've pretty-much mastered class-based components.

On a more subjective level, I have a really hard time whenever I'm doing "Pattern X" and then someone comes along (like Dan Abramov) and says, "Yeah... that other way you're doing everything sucks - so you should do it instead with 'Pattern Y'." To be clear, I'm always open to reading/hearing about new innovations. But when I see the "innovation", I need to see some compelling reason to switch. And no, the reason can't simply be, "The Old Method is stooopid and the New Method is awesome - so use the new method." That kinda "reasoning" is a quick way to turn me off to the new concept.

I've now done a fair amount of work converting class-based components to function-based components with Hooks. For the vast majority of these "refurbished" components, when I'm done with the conversion, I sit back and look at the difference and think, "Yeah... it's basically the same thing, with just a modestly-different syntax." And by now, there are many blogs/tutorials/etc that purport to show you how your old, stodgy class-based components can be converted to Hooks. Inevitably, the author drones on about how much "better" the Hooks version is. They declare it to be "cleaner". And yet... it looks damn-near the same.

But like so many of the other elitist trends in the JavaScript community, once the fanboys decide that Pattern Y is just clearly superior to Pattern X, there's no reasoning with them. To be honest, I've kinda given up the theoretical fight, because I'm tired of someone (e.g., hiring managers) looking down their noses at my horrible, ugly, unconscionable code that uses class-based components. If I showed a lot of React devs how I've managed to delivered quantum computing in JavaScript - but I did it in (egads!) a class-based component, there are just too many fanboys out there who will scrunch up their face and look at my revolutionary code as though it's the digital equivalent of a fart.

FWIW, I wrote an entire post about the odd demonization of the class keyword in JavaScript:


Another thing that I don't particularly like about Hooks is that they feel very much like an all-or-nothing proposition. Yes, I know that you can use functional, Hook-based components in the same codebase with old-fashioned class-based components, but I've already seen that run into a series of headaches that I would've never had to deal with if the Hooks Cartel hadn't decided that "Classes are bad... mmmkay???"


Shifting the way I think about components, from lifecycles to instances, especially when converting large components that rely on those lifecycles.
The end result is always clearer and easier to read code. Getting there requires some thought.


Could you clarify what you mean by "instances?"


What I mean is, rather than thinking “should this component update?” I instead can shift my thinking to “my props have changed, how should this instance of my component react to them?”


The part that got me (reminded me of because of this comment) that hooks don't share "states", but it's for sharing "logics".

Another one being useEffect. I still refer back to A Complte Guide to useEffect often.
useEffect still has clicked 100% especially on parts with getting new fresh value (using ref).


I hate how useEffect works.

I'd prefer having lifecycle back.

My main reason?

Is a hook that has many concerns. I prefer to think of functions as things that does only one thing right and nothing else.

I've tried a PoC for the project I'm working on and useEffect had very tricky behaviors at first.

Just think a bit about it:

If you want to do something on every render:


If you want to trigger something based on change of only one variable:

useEffect(<function>, [variableToWatch])

If you want to trigger on mount:

useEffect(<function>, [])

If you want to trigger something on unmount:

  () => {
    return  () => {

      // Some cleanup code here
}, []);

And what about cleanup functions?

Do you think they behave the same on all the cases?

Oh, my dear friend let me tell you that you're totally wrong.

As I said before, this works on component unmount:

  () => {
    return  () => {

      // Some cleanup code here
}, []);

And being honest I don't know when/why is the cleanup function is called here:

  () => {
    return  () => {

      // Some cleanup code here

That's my two cents about it.


I've been able to keep state closer to the component but I'm not sure if that's a great paradigm? Not sure. Maybe I am using hooks incorrectly, but I am making smaller api calls in the component than at a HOC and being able to reuse that data like when using redux. Is that good? Also, having trouble figuring out using hooks to its highest ability. Right now, it's nice to useState instead of having to care about this.state or this.setState or this in general but how to design components truly reusable. I saw a good example with an infinteScroll example hook but thinking about it on my own is difficult.
There are also no best practices (I think) so refactoring class components to functional, I don't know the benefits aside of it being more modular


I like using hooks. I don't like too much using classes. I have learned React from the beginning using hooks, not classes. The most interesting thing about useEffect is how it can pass from two renders to infinite loop changing only a little bit the second dependency parameter. Or for example, if you use useReducer and dispatch an action, you must know that the effect of this dispatch will not be visible until the next render, so you have to put your code inside useEffect in order to take account for this dispatch. So you end up using a lot of useEffect. Sometimes it's difficult to tune the second argument for useEffect. As a project I made myself the minesweeper game using hooks. It was cool to do that and I learned quite a lot. Or sometimes doing little test projects with a lot of console.logs to see what is going on with renders. With time we can master it!


Accepting not being able to exit early/to use conditions around useEffect. It took a few days to get used to it, but normally you can just move the condition inside the effect.


Whenever I've worked with useEffect, sometimes I want certain effects to only run on mount or on update and useEffect runs on all state changes. I know there are ways to run effects only on mount (or update), but I do miss using the componentDidMount and componentDidUpdate lifecycle functions for easier readability.


Definitely don't ignore eslint here. 99% if the time if you do you'll have a bug.

In this case the bug is: what happens when the URL changes?


I still find it challenging working with useEffects, sometimes it feels like I've known a thing or two, some days later, am wondering to myself...

I haven't tried out the others, been on the useState and useEffect for a while now.


The hardest part for us has been a difficulting in structuring components around state management and testability.

Components with hooks make testing super hard for us (in a React Native context). We can not use mounting for testing, as we are in react native, and shallowly rendering a component does not run anything inside a useEffect hook.

Thus we had to resort to mocking our useEffect to test our code which is super fragile since it basically mocks out react itself.

Our best option seem to be to go for custom hooks only which we can mock out easily for component testing and mock useEffect for those. But that just leads to weird code where instead of gaining colocation and readability from hooks we get messiness.

So, we are largely sticking to classes. Little though we want to.


To understand the differences between useEffect vs useRef vs useCallback vs useMemo. The other ones like useState or useReducer are easier.


I'm stuck there too. I've read hundreds of times on documentation but it just doesn't click on my mind.


The set-function of. useState is a little over-simplified for me sometimes. Especially with form fields


Interesting. Could you clarify what you mean?


When I use useState for a form field I need to update more states to track value, error, overall form. Instead of updating one json object. Idk now that I think about it, maybe it's the same thing. I like hooks more and more but I'm still in transition

This is an excellent use case for useReducer, instead of useState. With that you also make it clearer what state you’re handling, and the actions that can be used to update that state.

I don't know if useReducer would help all that much TBH, but...

You know, the setState() function returned from useState() can be passed a function, to update the state, which receives the current state and you are expected to return the new state.

setState( state => ({ ...state, updateValue: true }) );

This can be great when dealing with forms.

const [formState, setFormState] = useState({
    name: '',
    age: null,
    level: 1

const handleChange = ({ target: { name, value } }) => {
    return setFormState( state => ({ ...state, [name]: value }) );

return (
        <input name="name" onChange={handleChange} />
        <input name="age" onChange={handleChange} type="number" />
        <select name="level" onChange={handleChange}>
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>

Here's that code a little expanded:

I feel useReducer() doesn't add all that much to state hooks, other than being familiar to redux users. I share some of the reasons for this point of view in this talk if you are interested:


Quick share:

I've found that if you are starting to learn React, it's easier to start with 0.60 and hooks, also simpler. But if you are used to work on <0.59 (like 99% of the tutorials out there), it is a bit harder to understand Hooks and stop thinking onMount and such.

I first learned Hooks from the video at React Conf for the release of 0.60,
straight from the guys who designed it.


I learnt React hooks first, so it was hard to understand React.components later


Hi I am totally new to react. Can you explain what are the benefits of hooks over class in layman terms.


Watch the video on the first page of the hooks docs: reactjs.org/hooks


The use practical use case of some built-in hooks is not clear (useLayoutEffect for instance).

Another pain point is testing hooks


Have you seen this yet? kentcdodds.com/blog/useeffect-vs-u...

Also, for testing, if you write your tests well, then they shouldn't have to change between classes and hooks: kentcdodds.com/blog/react-hooks-wh...


seems like a very interesting discussion. I'm glad you made it :)


Are components faster than "hooked" functions?


I believe there's been some research on this and I believe it depends on what you mean by "faster".

Function "components" result in smaller bundle sizes compared to class components since babel does less work on those from what I've seen, which results in (theoretically) faster download times when serving bundled js to a client.

I'm not sure about SSR or bench marks between the two in terms of computational time.


It is not amazing. And the hardest part for me is swallowing its workaround/hacky nature


The exposure of foot-guns upfront. Thinking about rendering performance almost immediately (I know, I know... measure first)