DEV Community

Cover image for Demystifying React Hooks: useReducer
Milu
Milu

Posted on

Demystifying React Hooks: useReducer

React Hooks are a simpler way to encapsulate stateful behavior and side effects in functional components instead of classes. Some hooks are easier to understand than others, therefore this series of posts will focus on demystifying the hooks that are not as straightforward.

So far, we have explored useCallback, useMemo, useRef, and useContext so make sure to checkout my previous posts if you haven’t already. This week, let’s start with the basics by explaining the JavaScript reduce method. Once we explore the basics, it will be much easier to understand the useReducer hook, as well as how and when to use it in your code.

What is a reducer?

Alt Text

A reducer is the action that will be executed in order to get only one value. The goal of a reducer is to reduce (...duh!). The value returned could be a number, a string, an array or even an object, as long as it is a singular value. Moreover, it is important to highlight that reducers return a new value instead of mutating your initial value.

reduces are very useful when you want to obtain a single value after applying some logic to a group of values. For instance, if you want to add up an array of numbers to obtain a total value as we do in the following example.

We apply the JavaScript reduce method to an array of numbers called nums = [1,2,3,4,5]. The reduce method takes two parameters:

reducer - a function that provides instructions to obtain one value. In this case, to sum up all the given values in the nums array.

const reducer = (accumulator, currentValue) => accumulator + currentValue;
Enter fullscreen mode Exit fullscreen mode

initialValue - the starting point value when implementing the reducer function's instructions. In our example, we define our initial value as 0 so the total value returned reflects only the sum of the values in the array nums.

const initialValue = 0;
Enter fullscreen mode Exit fullscreen mode

Now that we defined the elements involved, let’s see it all together. The reduce method takes our initialValue and builds on it by following the instructions given under the reducer function, adding each value in the nums array until it is able to return one total value.

// reducer method - the action that will be executed in order to get one value
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// array of values that we want to add up
const nums = [1,2,3,4,5];

// initial value set to 0
const initialValue = 0;

// JavaScript reduce method receives two parameters: the reducer function and initial value defined above
const totalValue = nums.reduce(reducer, initialValue);
Enter fullscreen mode Exit fullscreen mode

What is useReducer()?

The useReducer hook is used with state management. It receives the following parameters:

reducer - a function that provides instructions on how to manage state. It takes two parameters state and action and it returns a new state.

// reducer type
(state, action) => newState
Enter fullscreen mode Exit fullscreen mode

initialState - the starting point value. It will change according to the reducer instructions.

Does it look familiar? Well yeah... It takes similar parameters as the reduce function explained above. However, the useReducer hook does not return just one value as reduce does. Instead it returns two elements as an array, the current state and a dispatch function.

const [state, dispatch] = useReducer(reducer, initialState);
Enter fullscreen mode Exit fullscreen mode

If you are familiar with React hooks, you probably have used useState before. Let’s compare these two hooks

// useState implementation
const [state, setState] = useState(initialState);

// useReducer implementation
const [state, dispatch] = useReducer(reducer, initialState);
Enter fullscreen mode Exit fullscreen mode

useReducer and useState both return a stateful value (state), and a function to update the state (setState and dispatch). In addition, both hooks receive an initial state value (initialValue). The main difference in these two initializations is that useReducer also takes a reducer function, which will be called when we use the returned dispatch function. Let’s explore how useReducer works in the next section.

How to use useReducer?

Alt Text

Sometimes the best way to explain how something works is with an example so let’s take a look at one. Here is a definition for an initialState, also called a store of data, that contains a list of dogs up for adoption with their name, breed, and adoption status.

const initialState = [
 {
      name: "Waffles",
      breed: "Chihuahua",
      adopted: false,
  },
  {
      name: "Charlie",
      breed: "Pitbull",
      adopted: true,
  },
  {
      name: "Prince",
      breed: "German Shepherd",
      adopted: false,
  },
];
Enter fullscreen mode Exit fullscreen mode

Now let’s create a reducer function to update our initialState list of dogs as they get adopted or returned. This function takes the following parameters:

state - the current state of our dogs in adoption.

action - an object that contains the following:

  • type of action we want to perform. In this case, we are only building two options, adopt or ‘return’.

  • payload optional data. In our example, we will pass the dog’s name so we can identify which dog got adopted or returned.

const reducer = (state, action) => {
  switch (action.type) {
    case 'ADOPT':
      return state.map(dog => {
        if (dog.name === action.payload) dog.adopted = true;
        return dog;
      });
    case 'RETURN':
      return state.map(dog => {
        if (dog.name === action.payload) dog.adopted = false;
        return dog;
      });
    default:
      return state;
  }
}
Enter fullscreen mode Exit fullscreen mode

Now It’s finally time to implement our useReducer() hook! Take a look at the example below, where we define our useReducer() hook with the initialState (adoption dogs list) and the reducer function we created to handle their adoption status.

We iterate through our dogs list state and display a button that will say adopt or return depending on their current adoption status. The onClick handler assigned to our button will call a function in charge of using the dispatch function returned by our useReducer() hook passing the type of action it needs to perform and the dog’s name as payload.

const adoptDog = name => dispatch({ type: 'ADOPT', payload: name });
Enter fullscreen mode Exit fullscreen mode

The dispatch function will pass this data to our reducer function, where we will use the type to identify what section of code needs to run and the payload to find the dog record we need to update.

When should you use useReducer?

When explaining the useReducer hook, we compared it to useState so you might be wondering… when should I use useReducer and when should I use useState?

The useReducer hook is a preferred alternative to useState when dealing with the following:

  • Complex state logic that involves multiple sub-values
  • State values that depend on the state of other state elements

Summary

  • The JavaScript reduce method is very useful when you want to obtain a single value after applying some logic to a group of values.

  • reducers return a new value instead of mutating your initial value.

  • The useReducer hook is used with state management.

  • The useReducer hook should be used when dealing with complex state logic, multiple sub-values, or when your state depends on state sub-values.


I hope this post helped you get a better understanding of the JavaScript reduce method and the useReducer() hook and that you will start taking advantage of these concepts in your future projects.

I enjoy creating content that explains concepts in really simple terms. Why? Because knowledge is power and I want to help beginner developers expand their knowledge and thrive.

Give this post a reaction if you found it helpful and follow me on Twitter and Dev.to to keep up with new posts!

Top comments (8)

Collapse
 
davidyaonz profile image
David Yao

This is one of the best explanations of hooks. Excellent work.

Collapse
 
milu_franz profile image
Milu

Thank you David! :)

Collapse
 
adriansamuel profile image
Adrian Samuel

Your illustrations are life!

I like how you’ve wrapped your dispatches in a functions!
Will be using that for modularity :)

Collapse
 
milu_franz profile image
Milu

Thank you Adrian! I really appreciate the kind words :)

Collapse
 
__victorchan profile image
Victor Chan

Greatly written, thanks!

Collapse
 
milu_franz profile image
Milu

Thanks Victor! :)

Collapse
 
ndkv9 profile image
Erik Nguyen

Thanks for the tutorial, that would be great if you can make another one that instructs how to combine useContext and useReducer, :D

Collapse
 
anj1n profile image
anj1n

Nice texts! It'll be good to see an article about the useReducer with a context and about their optimization :)