React JS: useReducer

ReactJS hooks

reactjs
hooks
useReducer
description
Author

albertprofe

Published

Tuesday, June 1, 2021

Modified

Tuesday, September 26, 2023

📘 useReducer

useReducer is a React Hook that lets you add a reducer to your component.

const [state, dispatch] = useReducer(reducer, initialArg, init?)


A reducer is a function that takes in the current state of an application and an action, and returns a new state. Reducers are often used in conjunction with a state management library like Redux, which helps manage the state of a React application.

App.js
function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

Reducers are a powerful tool for managing the state of a React application, and are often used in conjunction with actions and a state management library like Redux or useReducer hook.

In this example, the reducer function takes in a state object and an action object, and returns a new state based on the action type.

Although reducers can reduce the amount of code inside your component, they are actually named after the reduce() operation that you can perform on arrays.

The reduce() operation lets you take an array and accumulate a single value out of many:

App.js
const arr = [1, 2, 3, 4, 5];
const sum = arr.reduce(
  (result, number) => result + number
); // 1 + 2 + 3 + 4 + 5

The function you pass to reduce is known as a reducer.

It takes the result so far and the current item, then it returns the next result. React reducers are an example of the same idea: they take the state so far and the action, and return the next state.

In this way, they accumulate actions over time into state.

1 useReducer(init)

useReducer(reducer, initialArg, init?)

Call useReducer at the top level of your component to manage its state with a reducer.

App.js
import { useReducer } from 'react';

function reducer(state, action) {
  // ...
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });
  // ...

Consolidate state logic with a reducer: a reducer function is where you will put your state logic.

Consolidate state logic with a reducer: a reducer function is where you will put your state logic.

1.1 Parameters

  • reducer: The reducer function that specifies how the state gets updated. It must be pure, should take the state and action as arguments, and should return the next state. State and action can be of any types.
  • initialArg: The value from which the initial state is calculated. It can be a value of any type. How the initial state is calculated from it depends on the next init argument.
  • optional init: The initializer function that specifies how the initial state is calculated. If it’s not specified, the initial state is set to initialArg. Otherwise, the initial state is set to the result of calling init(initialArg).

2 dispatch function

useReducer returns an array with exactly two values:

  1. The current state. During the first render, it’s set to init(initialArg) or initialArg (if there’s no init).
  2. The dispatch function that lets you update the state to a different value and trigger a re-render.

The dispatch function returned by useReducer lets you update the state to a different value and trigger a re-render.

You need to pass the action as the only argument to the dispatch function:

App.js
const [state, dispatch] = useReducer(reducer, { age: 42 });

function handleClick() {
  dispatch({ type: 'incremented_age' });
  // ...

useReducer returns an array with exactly two values: the current state and the dispatch function

useReducer returns an array with exactly two values: the current state and the dispatch function
Warning

dispatch functions do not have a return value.

3 Example 1

In this example, the reducer manages a state object with one field: age.

App.js
import { useReducer } from 'react';

function reducer(state, action) {
  if (action.type === 'incremented_age') {
    return {
      age: state.age + 1
    };
  }
  throw Error('Unknown action.');
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });

  return (
    <>
      <button onClick={() => {
        dispatch({ type: 'incremented_age' })
      }}>
        Increment age
      </button>
      <p>Hello! You are {state.age}.</p>
    </>
  );
}

useReducer inital render

useReducer inital render

useReducer: handler > dispatcher > useRducer > reducer > update state > render new state

useReducer: handler > dispatcher > useRducer > reducer > update state > render new state

Be careful with that: how to update the new state:

App.js
// ..
    return {
      age: state.age + 1
    };

// ...

This will be a mistake:

age = state.age + 1

state.age = state.age + 1

age = age + 1

State is read-only. Don’t modify any objects or arrays in state: instead, always return new objects from your reducer.

useReducer is very similar to useState, but it lets you move the state update logic from event handlers into a single function outside of your component. Read more about choosing between useState and useReducer.

4 Example 2

In this example, the reducer manages a state object with two fields: name and age.

App.js
import { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      return {
        name: state.name,
        age: state.age + 1
      };
    }
    case 'changed_name': {
      return {
        name: action.nextName,
        age: state.age
      };
    }
  }
  throw Error('Unknown action: ' + action.type);
}

const initialState = { name: 'Taylor', age: 42 };

export default function Form() {
  const [state, dispatch] = useReducer(reducer, initialState);

  function handleButtonClick() {
    dispatch({ type: 'incremented_age' });
  }

  function handleInputChange(e) {
    dispatch({
      type: 'changed_name',
      nextName: e.target.value
    }); 
  }

  return (
    <>
      <input
        value={state.name}
        onChange={handleInputChange}
      />
      <button onClick={handleButtonClick}>
        Increment age
      </button>
      <p>Hello, {state.name}. You are {state.age}.</p>
    </>
  );
}

useReducerinital render

useReducerinital render

useReducernew render

useReducernew render