DEV Community

Cover image for Understanding the useReducer Hook in React
Manav Bajaj
Manav Bajaj

Posted on

Understanding the useReducer Hook in React

In the world of React, managing state is a fundamental aspect of building dynamic and interactive applications. One of the most commonly used tools for this purpose is the useState hook, which allows us to manage a component's state in a simple and intuitive way.

The useState function returns an array with two elements: the current state value and a state setter function. When the setter function is called with a new value, React triggers a re-render, and the component updates to reflect the new state. Typically, we define event handlers that encapsulate our state update logic. These handlers call the setter function with the updated value, enabling our components to respond to user interactions or other changes in application data.

import React, { useState } from 'react'; 

const [count, setCount] = useState(0); 
// 'count' is the reactive state variable
// 'setCount' is the function used to update the state
// Calling 'setCount' triggers a re-render with the new state

function addCount(){
  let new_count = count+1;
  setCount(new_count)
}

// an event handler that encapsulates the state update logic
// further calls the state setter function with the updated value

Enter fullscreen mode Exit fullscreen mode

As your application grows in size and complexity, your components will often require multiple state updates. This can lead to your components having event handlers with increasingly complex state management logic, making it harder for others (or even your future self) to quickly understand how the component's state is being modified.This is where the useReducer hook comes to the rescue,offering a more structured and scalable approach to state management.

The useReducer hook is inspired by the reducer pattern popularized by Redux, allowing you to manage state through a reducer function that specifies how the state should change in response to dispatched actions. This not only centralizes your state logic but also makes it easier to test and maintain.

In this article, we'll delve into the useReducer hook, exploring its syntax, usage, and benefits through practical examples. Whether you're a beginner looking to expand your React toolkit or an experienced developer seeking to optimize your state management strategies, this guide will provide valuable insights into leveraging useReducer effectively.

What is useReducer?
The useReducer hook is a built-in React hook that provides an alternative way to handle state in functional components. It is particularly useful when dealing with complex state logic that involves multiple sub-values as it extracts the state management logic out of the component.

The useReducer(reducer, initialState) hook accepts 2 arguments: the reducer function and the initial state. The hook then returns an array of 2 items: the current state and the dispatch function.

Basic Syntax
To use useReducer, you first need to import it from React:

import {useReducer} from 'react';
Enter fullscreen mode Exit fullscreen mode

Then, you define your reducer function, which specifies how the state should change in response to the dispatched actions:

function reducer(state, action) {
  switch (action.type) {
    case 'actionType1':
      // return new state
    case 'actionType2':
      // return new state
    default:
      throw new Error('Unknown action type');
  }
}
Enter fullscreen mode Exit fullscreen mode

Next, you call useReducer in your component, passing the reducer and the initial state:

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

Here, state holds the current state, and dispatch is a function that you can call with an action to update the state.

Simple Example: Counter
Let’s explore useReducer with a simple counter example to illustrate its mechanics.

import {useReducer} from 'react'

function App(){
  // const [count,setCount] = useState(0)
  const [count,dispatch] = useReducer(reducer,0)

  function addCount(){
    dispatch({type:'add'})
  }

  function subCount(){
    dispatch({type:'subtract'})
  }
  return(
    <div>
      {count}
      <button onClick={addCount}>+</button>
      <button onClick={subCount}>-</button>
    </div>
  )
}

function reducer(state,action){
  switch(action.type){
    case 'add': return state+1;
                break;
    case 'subtract': return state-1;
                     break;
  }
}

export default App;

Enter fullscreen mode Exit fullscreen mode

How It Works

  • The reducer function handles two actions: add and subtract, updating the count state accordingly.
  • useReducer returns the state (e.g., { count: 0 }) and dispatch, which triggers state updates via actions.
  • Clicking the buttons dispatches actions, and the reducer computes the new state, which React uses to re-render the component.

This example mirrors how useState updates state but organizes the logic in a reducer, making it easier to extend for more complex scenarios.

How useReducer is Built on useState
The useReducer hook is fundamentally built on top of useState, leveraging its state management capabilities to provide a more structured approach to handling complex state logic. To understand this, consider a simplified implementation of useReducer:

import { useState } from 'react';

export function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);
  const dispatch = (action) => {
    const updated_state = reducer(state, action);
    setState(updated_state);
  };
  return [state, dispatch];
}
Enter fullscreen mode Exit fullscreen mode

The useReducer uses useState internally to store the state and trigger re-renders. The state variable holds the current state, just like in useState. The dispatch function calls the reducer with the current state and an action, computes the new state, and uses setState to update it, prompting React to re-render the component. The key difference is that useReducer introduces the reducer function, which encapsulates state transition logic, making it ideal for complex scenarios where multiple state updates need to be coordinated.

When to Use useReducer
While useState is suitable for simple state updates, useReducer is preferable in the following situations:

  • Complex State Logic: When your state has multiple sub-values that are interdependent or when the state transitions are complex.
  • Next State Depends on Previous State: When the next state relies on the previous state in a way that's not straightforward with useState.
  • Centralized State Logic: When you want to keep all state update logic in one place, making it easier to understand and maintain.

Conclusion
The useReducer hook is a powerful tool for managing complex state in React, built on the foundation of useState but offering a structured approach through reducers and actions. By centralizing state logic, it makes code predictable and maintainable, as seen in the to-do list example. With useReducer, you’re well-equipped to tackle complex state challenges in React!

Top comments (0)