React useReducer hook – Tutorial and Examples

Last updated on January 3, 2021 Guest Contributor Loading... Post a comment

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 (one simple, one complex) of using the useReducer hook in action.

Overview

The useReducer hook allows you to manage 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 set 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.

A Simple Example: useReducer + form input

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 enable 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.

Preview:

The code with useReducer:

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

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

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

  // This function is triggered 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.value);
    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 gives the same result:

// 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 a bit longer.

A Complex Example

In this example, we are going to create a fried chicken ordering application. Users can buy 1 or more fried chicken set with options including Coke, 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.

To learn more about React and React Native, check out our React topic page and React Native topic page for more tutorials and examples.

Related Articles

guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x