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.

 

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.

 
 

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.

 

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.

Example:

const useRelatedState = (
  items,
  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 => {
      ref.current(mapRelatedState);
    },
    [ref]
  );

  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(() => {
    fetchMuppets().then(setMuppets);
  }, []);

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

...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', () => {
  afterEach(cleanup);

  test('async data loading', async () => {
    const { getByText } = render(<AsyncDataList />);
    await wait(() => getByText('Kermit'));
    expect(getByText('Kermit')).toBeInTheDocument();
  });
});
/*
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()

 

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.

 
 

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).

 
  • 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.
 
 

Some junior developers don't bother to learn them, and they stick to inefficient and messy classes. This is pretty sad, as I put in quite a lot of effort to drive the amount of HOCs in the codebase as low as possible, so people don't have to deal with old messy react.

The sad part is that hooks are much simpler to understand, use, and they result in code that's better in every single aspect. After reading through proposal and docs for it, writing hooks felt natural and easy to me. It's a much better API that gets closer to what I saw in react when I used it for the first time.

 

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:

useEffect(<function>)

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:

useEffect(
  () => {
    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:

useEffect(
  () => {
    return  () => {

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

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

useEffect(
  () => {
    return  () => {

      // Some cleanup code here
    }
};

That's my two cents about it.

 
[deleted]
 

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?

 

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.

 

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.

 

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.

 

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.

 
 

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 (
    <form>
        <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>
        </select>
    </form>
  );

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:
youtube.com/watch?v=gRXgE2iHRek

 

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

 

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

 

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

 
 

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...

Classic DEV Post from Jun 15

What are your favourite resources for beginners?

What resources would you give a new team member who is also starting their first tech job?

Kent C. Dodds profile image
My name is Kent C. Dodds and I'm a JavaScript engineer and teacher. I'm also active in the open source community. I like my family, JavaScript, and React.

dev.to now has dark mode.

Go to the "misc" section of your settings and select night theme ❤️