Categories
React

A Quick Intro to useReducer() (with an Example)

useState is a nice and simple hook for managing state in functional components (as opposed to setState in class components). But as the complexities of your application grow, you’ll find yourself needing something a little more powerful.

Say “hello” to useReducer!

What is useReducer, and what problems does it solve?

useReducer is a React hook used to manage state in your application. It’s comparable to useState, but is designed for more complex requirements and so works in a slightly different way (more on that later).

useState reaches its limit of being a simple, easy-to-use state management hook when you get into the arena of interdependent states or needing multiple sub-values. It just isn’t great for these use cases.

In the case of interdependent state, say you want to set one piece of state based on the value of another piece. With useState there’s a risk (albeit a small one) that the setting function would be based on old state because the state update wasn’t processed in time. This is a prime opportunity for useReducer.

The other opportunity to utilise the benefits of useReducer is when you have multiple pieces of state that are related. For example, a bunch of state related to the same <form>. We can use the useReducer hook to essentially merge and group together those separate pieces of state, resulting in more readable code.

In summary:

  • useState is a good starting point for state management, it’s great for:
    • Independent pieces of data
    • Simple updates to state
  • useReducer is a more complex state management solution which is great for:
    • More powerful state-setting logic
    • Grouping and updating related data / state

How does useReducer work?

useReducer comes in two main parts; the hook creation/setup itself and its reducer function.

Setting up the hook

Inside your component, drop in useReducer() where you manage your other state. As you can see below, it looks a lot like useState().

const [state, dispatchFn] = useReducer(reducerFn, initialState, initFn);

Let’s break it down:

  • state – this is the state that you’d use across your component (in your functions and in rendering to the UI)
  • dispatchFn – this is a function that can be used to trigger an update of the state
  • reducerFn – this is a custom function that you can define outside of your component which takes the latest state and returns the new, updated state
  • initialState – this is, well… the initial state
  • initFn – this allows you to set the initial state programmatically with a function

The reducer function

We looked at reducerFn above, but we can see what it looks like below.

(prevState, action) => newState

So, as we said, the reducer function takes the previous state (prevState). It also takes the new data being passed to it in action. Finally, it must return the new state (newState) to be stored in the useReducer() hook from before.

Example of useReducer usage

Here’s a simple example that is far better at showing how to use it than it is at showing what you might actually end up using it for. It shows how you can use one state setting function – inputReducer() – from calling dispatchChange() on the event to dynamically set the appropriate <input>-connected state.

import React, { Fragment, useReducer } from "react";

const inputReducer = (prevState, action) => {
  const inputName = `input${action.id}`;
  return { ...prevState, [inputName]: action.value };
};

const ReducerExample = () => {
  const [state, dispatchChange] = useReducer(inputReducer, {
    input1: "",
    input2: "",
    input3: "",
  });

  const handleChange = (event) => {
    event.preventDefault();
    dispatchChange({ id: event.target.dataset.id, value: event.target.value });
  };
  return (
    <Fragment>
      <h2>Reducer Example</h2>
      <div>
        <input type="text" onChange={handleChange} data-id="1" />
        <input type="text" onChange={handleChange} data-id="2" />
        <input type="text" onChange={handleChange} data-id="3" />
      </div>
      <p>{`${state.input1}${state.input2}${state.input3}`}</p>
    </Fragment>
  );
};

export default ReducerExample;

Leave a Reply

Your email address will not be published. Required fields are marked *