Exploring React Hooks: useReducer

Exploring React Hooks: useReducer

Exploring the Power of useReducer Hook in React

Welcome back to our Exploring React Hooks series! In our previous article, we explored the useRef Hook. Today, we will explore another powerful hook called useReducer.

Let's begin..!!

🪝What is the useReducer Hook?

The useReducer hook is a powerful state management tool in React that simplifies complex state logic by utilizing a reducer function.

Syntax:

The syntax for useReducer is as follows:

const [state, dispatch] = useReducer(reducer, initialState);
  • state represents the current state managed by useReducer.

  • dispatch is a function used to trigger state updates.

  • reducer is a function that receives the current state and an action and returns the new state.

  • initialState is the initial value of the state.

Working with Reducers:

A reducer is a pure function that takes in the current state and an action as parameters and returns the new state. It follows the following structure:

function reducer(state, action) {
  switch (action.type) {
    case 'ACTION_TYPE':
      // Perform state transition and return the new state
    default:
      return state;
  }
}

In the reducer function, we typically use a switch statement to handle different action types and define how the state should transition accordingly. You must remember to always return the current state as the default case to ensure that unhandled actions do not modify the state unintentionally.

Dispatching Actions:

To update the state managed by useReducer, we use the dispatch function. This function takes an action object as an argument and triggers the execution of the reducer function.

dispatch({ type: 'ACTION_TYPE', payload: value });

The type property is mandatory and represents the type of action being dispatched. Additional properties, such as payload, can be included to pass any necessary data to the reducer.

🪝Example 1: Managing Simple State & Action

Let's create a component CounterReducer. We'll set the initial state of the counter to 0. Next, import the useReducer Hook.

Inside the reducer function, when the action is:

increment : the state will be incremented by 1.

decrement : the state will be decremented by 1.

reset : the state will be reset to the initial value.

For any other action type, the current state will be returned as is.

Within the CounterReducer component, the useReducer hook is used to handle state management. It takes the reducer function and initialState as arguments and returns the current state (count) and a function (dispatch) to update the state by dispatching actions to the reducer.

import React, { useReducer } from 'react'

const initialState = 0
const reducer = (state, action) => {
    switch (action) {
        case 'increment':
            return state + 1
        case 'decrement':
            return state - 1
        case 'reset':
            return initialState
        default:
            return state
    }
}

function CounterReducer() {
    const [count, dispatch] = useReducer(reducer, initialState)

    return (
    <div>
      <div>Count = {count}</div>
            <button onClick={() => dispatch('increment')}>Increment</button>
            <button onClick={() => dispatch('decrement')}>Decrement</button>
            <button onClick={() => dispatch('reset')}>Reset</button>
        </div>
    )
}

export default CounterReducer

In the JSX code, we render the current count value using {count}. Three buttons are rendered: "Increment," "Decrement," and "Reset." Each button has an onClick event handler that dispatches the corresponding action to the reducer when clicked.

The CounterReducer component enables users to increment, decrement, or reset the counter value by dispatching different actions to the reducer. The reducer function processes these actions to determine the new state, which is then displayed in the output.

🪝Example 2: Managing Complex State & Action

Let's create another component CounterReducerTwo. We'll set the initial state of the firstCounter to 0 and the secondCounter to 1.

If the action type is "increment", the firstCounter property in the state will get incremented by the value specified in the action, while maintaining the other properties of the state using the spread operator.

Similarly, if the action type is "decrement", the firstCounter property will get decremented by the specified value.

The "increment2" and "decrement2" actions follow a similar pattern but operate on the secondCounter property instead.

And as before, if the action type is "reset", the state will be set back to the initialState object. Also, if the action type does not match any of the defined cases, the current state is returned as is.

import React, { useReducer } from "react";

const initialState = {
  firstCounter: 0,
  secondCounter: 10
};
const reducer = (state, action) => {
  switch (action.type) {
    case "increment":
      return { ...state, firstCounter: state.firstCounter + action.value };
    case "decrement":
      return { ...state, firstCounter: state.firstCounter - action.value };
    case "increment2":
      return { ...state, secondCounter: state.secondCounter + action.value };
    case "decrement2":
      return { ...state, secondCounter: state.secondCounter - action.value };
    case "reset":
      return initialState;
    default:
      return state;
  }
};

function CounterReducerTwo() {
  const [count, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <div>Count - {count.firstCounter}</div>

      <button onClick={() => dispatch({ type: "increment", value: 1 })}>
        Increment
      </button>
      <button onClick={() => dispatch({ type: "reset" })}>Reset</button>
      <button onClick={() => dispatch({ type: "decrement", value: 1 })}>
        Decrement
      </button>

      <div>Secound Counter - {count.secondCounter}</div>

      <button onClick={() => dispatch({ type: "increment2", value: 5 })}>
        Increment 5
      </button>
      <button onClick={() => dispatch({ type: "decrement2", value: 5 })}>
        Decrement 5
      </button>
    </div>
  );
}

export default CounterReducerTwo;

In the JSX code, we render the current count value of the firstCounter using {count.firstCounter}. Clicking the

  • The "Increment" button dispatches the action { type: "increment", value: 1 }, incrementing firstCounter by 1.

  • The "Decrement" button dispatches the action { type: "decrement", value: 1 }, decrementing firstCounter by 1.

We also render the current count value of the secondCounter using {count.secondCounter}. Clicking the

  • The "Increment5" button dispatches the action { type: "increment2", value: 5 }, incrementing secondCounter by 5.

  • The "Decrement5" button dispatches the action { type: "decrement2", value: 5 }, decrementing secondCounter by 5.

Clicking the "Reset" button dispatches the action { type: "reset" }, resetting the state to the initialState for both counters.

The rendered output shows the current values of firstCounter and secondCounter, and the counters can be incremented, decremented, or reset based on the corresponding button clicks.

🪝Benefits of useReducer:

  • Simplified State Management.

  • Centralized State Logic.

  • Testing and Debugging.

  • Compatibility with useContext.

To Summarize

The useReducer hook in React is basically an alternative to the useState Hook. We will explore the differences between useReducer and useState in an upcoming blog post.

The useReducer Hook simplifies state management by utilizing a reducer function to handle more intricate state transitions based on dispatched actions, providing a predictable and organized approach.


References


Thank's for taking the time to read this blog. Hope you found it informative and enjoyable! Connect with me on Twitter.

Stay tuned for our next article: useCallback Hook.

Catch you guys on the next one. Cheers .. ✌️

Did you find this article valuable?

Support Raj Sarkar by becoming a sponsor. Any amount is appreciated!