Introduction to useReducer: A React JS Hook

Avinash Ratnam
Nerd For Tech
Published in
5 min readMar 31, 2023
Photo by Gabriel Heinzer on Unsplash

Introduction

  • useReducer can be considered an advanced version of the useState hook, where the user can control the state variable value using a function.
  • However, useReducer differs from useState since it uses a reducer function. These reducer functions are used to influence the value of the reducer value.
  • The reducer function accepts two arguments, the state (current state value that will be updated) and the action (some value that influences how the ‘state’ changes).

Note: Generally, we use useReducer() with objects since they are more complex. Using useReducer with single variable types defeats its purpose since you may as well use useState.

Breaking down useReducer

A breakdown of the useReducer function call
A breakdown of the different parts of a typical useReducer definition.
const [state, dispatch] = useReducer(reducer, initialArg);

Some of the components of a useReducer call are:

  • const [state, dispatch] -> represents the state value and a dispatch function used to call the reducer function, respectively.
  • useReducer() -> The hook we are exploring today.
  • reducer -> A special type of function that is used to change the state variable. It accepts two arguments, the state, and action. ‘State’ represents the variable that is going to be changed. ‘Action’ is an object (generally) that influences how the state variable is changed.
  • initialArg -> The initial value assigned to the state variable.

A Simple useReducer example:

Code to increase the count by five or reduce by one using useReducer()

In this example:

  • Our state variable, state, is initialized with the object ‘{count: 0}’. The reducer function accepts two parameters, ‘state’ and ‘action.’
  • When the user clicks the ‘Increase Count’ button, ‘dispatch’ passes the object { type: “increase”, payload: 5 } to the reducer function (as the second parameter. The first parameter is always the state variable itself). Here, ‘type’ indicates what action will be performed on the state variable, and the ‘payload’ is the value that will be added to the state variable.
  • Hence, in the reducer function, we use a ‘switch’ statement to find the required action to perform, add the ‘action.payload’ value to the ‘state.count’ value, and return the updated value.
  • Similarly, when the user clicks the ‘Decrease Count’ button, ‘dispatch’ passes the object { type: “decrease” } to the reducer function. Here, ‘type’ indicates what action will be performed on the state variable, and since there is no payload, we reduce one from the ‘state.count’ value in the reducer function directly.

A complex example with useReducer and useState:

Link to CodeSandBox: https://codesandbox.io/s/lucid-edison-qocldp?file=/App.js

import { useReducer, useState } from "react";

const App = () => {
const reducer = (formVals, action) => {
switch (action.type) {
case "addUser":
return [...formVals, action.payload];
case "removeUser":
return formVals.filter((val) => {
return val.name !== action.payload.name;
});
default:
return formVals;
}
};

const [formVals, dispatch] = useReducer(reducer, []);
const [name, setName] = useState("");
const [password, setPassword] = useState("");

const updateUserList = (actionToDo) => {
console.log(actionToDo);
dispatch({
type: actionToDo,
payload: {
name: name,
password: password
}
});
};

return (
<>
<form onSubmit={(e) => e.preventDefault()}>
<label for={"userName"}>Name: </label>
<input
className={"userName"}
type={"string"}
value={name}
onChange={(e) => setName(e.target.value)}
></input>
<br />
<label for={"userPassword"}>Password: </label>
<input
type={"password"}
value={password}
onChange={(e) => setPassword(e.target.value)}
></input>
<button onClick={() => updateUserList("addUser")}>Add User</button>
<button onClick={() => updateUserList("removeUser")}>
Remove User
</button>
</form>
<div>
User Details:
{formVals.map((formData, i) => {
return (
<div key={i}>
Name: {formData.name} {" "}
Password: {formData.password}
</div>
);
})}
</div>
</>
);
};

export default App;

Program Summary: A program that updates the state variable with a list of objects, and the user can either ‘add’ users to the list or remove them based on their username.

Code Breakdown:

reducer function definition

The reducer function takes formVals and action as the two parameters.

Based on the action.type, it determines whether or not we are going to add action.payload (an object) to the formVals list, or

Remove that user from the formVals list of objects by filtering out all the other entries (based on the user name).

useReducer(), useState() and helper function definitions

The useReducer(), in this instance, has a reducer function called reducer and uses an initial value of an empty list [].

The useState() definitions use an empty string as the initial value for the name and password.

updateUserDetails() accepts actionToDo as a parameter and passes an object to dispatch(), with type and payload.

return statement definitions

We are rendering pretty basic DOM elements, a form element with a string and password input, which onChange() update the name and password states, respectively.

Finally, there are two buttons to add or remove a user from the list, with the button onClicks passing addUser and removeUser, respectively.

In the end, we are rendering the list of users and their passwords using formData.

Conclusion:

useReducer() provides the user with a more comprehensive way of interacting with their state object, providing more functionality than useState.

A big thank you to Ben Mullins, SI 579 — Interactivity with JavaScript professor at the University of Michigan School of Information, and the GSIs, Josh and April, for the opportunity!

References:

--

--

Avinash Ratnam
Nerd For Tech

I'm a graduate student at the University of Michigan, Ann Arbor. I have a keen interest in UX Design and Engineering. Welcome to my (attempt) at a Tech Blog!