Kinda Code
Home/React/React useReducer hook – Tutorial and Examples

React useReducer hook – Tutorial and Examples

Last updated: February 13, 2023

useReducer is a built-in React hook.

In this article, you will learn what the useReducer hook is, what it is used for, when to choose the useReducer hook, and when to choose the useState hook. To make it easier to understand than boring long sentences, we will also walk through 2 complete examples (the first one is simple, and the second one is a little bit more complex) of using the useReducer hook in action.

Overview

The useReducer hook allows you to manage the state in a functional component, and it also provides a function that updates the state and re-renders the component. It is very similar to the useState hook but has a few advantages in certain cases, which we will discuss later in the Choosing between useReducer and useState section.

const [state, dispatch] = useReducer(reducer, initialState);

Where:

  • state: the state returned by useReducer.
  • dispatch: a function used to update the state.
  • reducer: a function that takes 2 arguments: the first one is the previous state and the second one is an action which is an object containing information used for updating the state (This explanation can be a little confusing. Don’t worry, the examples given in this article will show it’s not that complicated).
  • initialState: the state at the beginning.

Choosing between useReducer and useState

In simple words:

  • useReducer is better when you have kind of connected and complex states. You can write some logic that basically runs whenever you want to change the state to do more complex updates than just setting a new value.
  • useState is better when your states are NOT complex and interdependent. In these cases, using useState makes your code much shorter and easier to understand than using useReducer.

Simple Example: useReducer + form input

The purpose of this example is to help you understand the basics of the useReducer hook and see that, many times, using useState is the better solution.

Preview

This example app contains a form with a text input and a button. The text input lets the user enter his / her name. The button is disabled by default and only becomes enabled when the entered name is valid (We assume that a name is valid when its length is greater than or equal to 3). When the button is clicked, the thing in the text input will be clear.

The code with useReducer

// App.js
import React, { useReducer } from 'react';

const App = () => {
  // The initial state
  const initialState = {
    name: '',
    isValid: false,
  };

  // The reducer function
  const [nameState, dispatch] = useReducer((state, action) => {
    switch (action.type) {
      case 'CHANGE':
        // Update the state
        return {
          name: action.value,
          isValid: action.value.length >= 3 ? true : false,
        };
      case 'SUBMIT':
        // Reset the state
        return {
          ...initialState,
        };
      default:
        return state;
    }
  }, initialState);

  // This function is called when the user types something
  const changeHandler = (event) => {
    dispatch({
      type: 'CHANGE',
      value: event.target.value,
    });
  };

  // This function is trigger when the user clicks the "submit" button
  const submitHandler = (event) => {
    event.preventDefault();
    alert(nameState.name);
    dispatch({
      type: 'SUBMIT'
    })
  };

  return (
    <form style={styles.container}>
      <input
        type="text"
        value={nameState.name}
        onChange={changeHandler}
        style={styles.input}
      />
      <button disabled={!nameState.isValid} onClick={submitHandler}>
        Submit
      </button>
    </form>
  );
};

export default App;

const styles = {
  container: {
    padding: 50,
  },
};

The code with useState (more concise)

// App.js
import React, { useState } from 'react';

const App = () => {
  const [name, setName] = useState('');
  const [isValid, setIsValid] = useState(false);

  // This function is triggered when the user types something
  const changeHandler = (event) => {
    const enteredName = event.target.value;
    setName(enteredName);
    if (enteredName.length >= 3) {
      setIsValid(true);
    } else {
      setIsValid(false);
    }
  };

  // This function is trigger when the user clicks the "submit" button
  const submitHandler = (event) => {
    event.preventDefault();
    alert(name);
    setName('');
    setIsValid(false)
  };

  return (
    <form style={styles.container}>
      <input
        type="text"
        value={name}
        onChange={changeHandler}
        style={styles.input}
      />
      <button disabled={!isValid} onClick={submitHandler}>
        Submit
      </button>
    </form>
  );
};

export default App;

const styles = {
  container: {
    padding: 50,
  },
};

Because the state, in this case, is simple, it seems that using useReducer will make the code more verbose.

Complex Example: When useReducer Shines

In this example, we are going to create a fried chicken ordering application. Users can buy 1 or more fried chicken sets with options including Coke and fried potatoes.

Note that the “-” buttons will be disabled when the item corresponding to it has a zero count

Preview

The complete code

// App.js
import React, { useReducer } from 'react';

const App = () => {
  const initialOrderState = {
    chicken: 0,
    coke: 0,
    fries: 0,
    total: 0,
  };

  const [orderState, dispatch] = useReducer((state, action) => {
    switch (action.type) {
      case 'CHICKEN':
        return {
          ...state,
          chicken: state.chicken + action.value,
          total: state.total + action.value * 3, // a chicken piece costs 3$
        };
      case 'COKE':
        return {
          ...state,
          coke: state.coke + action.value,
          total: state.total + action.value * 0.5, // a coke costs 0.5$
        };
      case 'FRIES':
        return {
          ...state,
          fries: state.fries + action.value,
          total: state.total + action.value, // a fries set costs 1$
        };
      case 'RESET':
        return initialOrderState;
      default:
        return state;
    }
  }, initialOrderState);

  const buttonHander = (type, value) => {
    dispatch({
      type: type,
      value: value,
    });
  };

  // This function is associated with the reset button
  const resetOrder = () => {
    dispatch({ type: 'RESET' });
  };

  // This function is associated with the order button
  const submitOrder = () => {
    alert(`You have submitted your order successfully - ${orderState.total}`);
  };

  return (
    <div style={styles.container}>
      <div>
        <p>Pricing: 3$ / chicken piece, 0.5$ / 1 Coke, 1$ / fries </p>
      </div>
      <hr />

      <div>
        <p>
          Fried chicken: {orderState.chicken} &nbsp;
          <button onClick={() => buttonHander('CHICKEN', 1)}>+</button>
          <button
            onClick={() => buttonHander('CHICKEN', -1)}
            disabled={orderState.chicken === 0}
          >
            -
          </button>
        </p>
        <p>
          Coke: {orderState.coke} &nbsp;
          <button onClick={() => buttonHander('COKE', 1)}>+</button>
          <button
            onClick={() => buttonHander('COKE', -1)}
            disabled={orderState.coke === 0}
          >
            -
          </button>
        </p>
        <p>
          Fired potats: {orderState.fries} &nbsp;
          <button onClick={() => buttonHander('FRIES', 1)}>+</button>
          <button
            onClick={() => buttonHander('FRIES', -1)}
            disabled={orderState.fries === 0}
          >
            -
          </button>
        </p>
        <br />
        <p>Total: {orderState.total}$</p>
        <p>
          <button onClick={resetOrder}>Reset</button>
          <button onClick={submitOrder}>Order</button>
        </p>
      </div>
    </div>
  );
};

export default App;

const styles = {
  container: {
    padding: 50,
  },
};

Conclusion

Congratulation! At this point, you should have a better understanding of the useReducer hook in React. You’ve learned about the syntax and when you should use it instead of the useState hook. The examples given in this article should also give you a better feeling of using it in practice.

Keep the ball rolling and continue exploring more new and exciting things about modern React development by taking a look at the following articles:

You can also check our React category page and React Native category page for the latest tutorials and examples.

Related Articles