useReducer Hook in React Explained with Simple Examples

useReducer Hook in React

In this tutorial, you will learn how to use React.useReducer() hook to manage complex state and state transitions in your components.

The useReducer() is an alternative to the more commonly used useState() hook. We use it when dealing with a state that involves multiple actions or requires more advanced logic.

Table of Contents

  1. What is useReducer?
  2. How useReducer works?
  3. When should I use useReducer over useState?
  4. Example 1: Implementing Counter application
  5. Example 2: Implementing a To-do list application
  6. Summary

What is useReducer?

useReducer(reducer, initialState) follows the reducer pattern found in JavaScript. The reducer function defines how the state should be updated based on the dispatched action. The hook then returns an array of 2 items: the current state and the dispatch function.

Now, let’s understand the terms initial state, action object, reducer, and dispatch.

A. initial state

The initial state refers to the value that is assigned to the state when it is first initialized.

In the below code, we set the initial value of the count to 0.

const [state, dispatch] = useReducer(reducer, { count: 0 });

B. action object

An action object is an object that provides a description of how the state should be updated. In the below example, we pass an action object with two types INCREMENT and DECREMENT.  Based on these two action types, the value of the count state is updated.

// Reducer function
switch (action.type) {
  case 'INCREMENT':
      return { count: state.count + 1 };
  case 'DECREMENT':
      return { count: state.count - 1 };

--snip--

C. reducer

The reducer is a pure function that takes two parameters: the current state and an action object. Within the reducer function, the state is updated in an immutable manner based on the action object, and then the new state is returned.

// Reducer function
const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      throw new Error('Unsupported action type');
  }
};

--snip--

const [state, dispatch] = useReducer(reducer, { count: 0 });

D. dispatch function

The dispatch is a specialized function used to dispatch an action object. For example,

const [state, dispatch] = useReducer(reducer, { count: 0 });

Whenever you need to update the state, typically triggered by an event handler or upon the completion of a fetch request. You can easily update it by calling the dispatch function dispatch(actionObject) with the corresponding action object.

For example,

const increment = () => {
  dispatch({ type: 'INCREMENT' });
};

const decrement = () => {
  dispatch({ type: 'DECREMENT' });
};

How useReducer works?

The below image shows how the state updates using useReducer hook.

How useReducer works in React

When should I use useReducer over useState?

As the application grows in size, handling more complex state transitions becomes hard. In such cases, I recommend using the useReducer hook.

The useReducer hook offers more predictable state transitions compared to useState. It is particularly valuable when the state changes become complex.

It is also used to optimize the performance of components that trigger deep updates. Instead of relying on callbacks for state updates, the dispatch function can be conveniently passed down.

For simple state management, I recommend using the useState hook. For more complex state management, the useReducer hook is the best choice.

Example 1: Implementing Counter application

Here is a complete example of how useReducer can be used to implement Counter application.

import React, { useReducer } from 'react';

// Reducer function
const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      throw new Error('Unsupported action type');
  }
};

// Component using useReducer
export default const Counter = () => {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  const increment = () => {
    dispatch({ type: 'INCREMENT' });
  };

  const decrement = () => {
    dispatch({ type: 'DECREMENT' });
  };

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

View Code: https://github.com/vilash99/usereducer-counter-application-react

In the above example, the Counter component uses the useReducer hook to manage the count state. The reducer function defines how the state should be updated based on the dispatched actions (INCREMENT and DECREMENT).

The state is initialized with an initial value of { count: 0 }. The dispatch function is used to trigger state updates by dispatching the corresponding actions.

Related: How to Create Your First ReactJS App: Hello World Example

Example 2: Implementing a To-do list application

In this example, we define a Index function that handles the state updates for our todo list. It takes the current state and an action object, and based on the action type, it performs the corresponding state update.

We initialize our state using the useReducer hook and pass in our Index function and an empty array as the initial state. The items array represents our list of todos.

We also use the useState hook to manage the input value for adding new todos. The item state holds the value of the input field, and we update it using the setItem function.

View Code: https://github.com/vilash99/usereducer-todo-list-in-react

When you run the above code, you will see an application similar to the below image in your browser.

React todo app demo

Summary

By using useReducer, you can handle more complex state updates and cover the related logic within the reducer function, making your code more organized and maintainable.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top