React useReducer hook

Ceci García García
Trabe
Published in
3 min readFeb 4, 2019
Photo by Vedant Choudhary on Unsplash

In my last post “React 16.7”, we had a look at the new React proposal version (which, by the way, it’s not a proposal for v16.7 anymore, but for v16.8) and a brief introduction to the hooks useState and useEffect. In this post we’ll address the useReducer hook.

Managing component state with the useState hook

useState is the basic hook that allows us to keep a state in a functional component. A basic usage example:

The updater function returned by the useState hook works much the same as the previous React setState function, except that the new one replaces the whole state with the argument passed instead of merging it with the old state.

Since we can use as many useState functions as we want, we can create and handle component state no matter its shape.

However, if our component has to keep several data which are all part of the same logic state, useState may become a bit cumbersome. Here is an example of a counter which can increment, decrement and reset its value and undo the last operation. To implement the undo operation, we need to keep the previous state in addition to the current state for every operation:

useReducer to the rescue!

useReducer is another hook to manage the state in a component. It looks like this:

const [state, dispatch] = useReducer(reducer, initialState);

The reducer function should be a function that receives a state and an action and (reduces it to) returns a new state: (state, action) => newState.

Below is the previous example but implemented using useReducer instead of useState:

This way, besides doing all state changes at once when an action is triggered, we can find all logic related with the same state placed together.

Dan Abramov wrapped up on a tweet the matter of when to use useState or useReducer to manage the component state:

Avoid passing callbacks down

With the useReducer hook, we can simplify the problem of passing callbacks down in large tree components. Here’s an example, quite contrived but we still can see the point we are talking about:

We could simplify the example above by defining an object which collects all callbacks so we can pass it down. To avoid the hell of passing down through every component in the hierarchy, we could pass the “api object” via context. The problem is that the object would change in every rerender, so all components that read it from the context would be rerendered as well.

Abramov recommended a similar pattern but, instead of passing down an “api object”, passing the dispatch function:

The key of this pattern is that the dispatch function doesn’t change between rerenders. So the final solution would look like this:

Wrapping up

You can use useState for managing any component state but there are two scenarios where useReducer is more convenient:

  • When the state keeps data which change together (like “data” and “isLoadingData”).
  • When you have to pass callbacks down in large component trees.

--

--