In this post, we'll cover React Reducers (with the
useReducer hook). This tutorial will help you understand:
what reducers are
why you need reducers at all
how to use the
how to easily implement them in your React code with TypeScript
know when to use advanced options like Redux and MobX
By the way, this is an intermediate React topic. We cover this more in depth in Fullstack React with TypeScript.
We just added a bunch of new content on how to use SSR with Next.js to the book. Check it out.
Why React Uses Reducers#
As our React applications grow into full-fledged interactive apps - the amount of data -- and state -- they work with increases.
Keeping track of state, and managing this state, creates a need for a layer of our app that deals solely with this important task.
This is where reducers come in.
What is a Reducer?#
All state management solutions perform some (or all) of these tasks:
keep track of the current state
take in new information that causes state modification
process the dispatched information and calculate a new state
return this new state so the application can use it
Reducers are a type of function that can perform these task for state management
Because Reducers are built into the React API, you don't necessarily have to use a separate state-management library like Redux or MobX.
Using reducer functions can lead to simpler code, as well as avoid the overhead of a separate state management library.
Suppose that we're tracking the name of a person in our state.
A reducer that manages this state must:
Remember the old state
Accept "dispatched" actions
Determine a new state and
Return a new state
Here is a diagram:
The dispatched information is called an "action." This reflects that some action or event in the application has happened, and that's why we must now update the state.
For example, think of an action as a user submitting a form, clicking on a button, selecting an option, or other actions that prompt us to change the state in the application.
Let's Create Our Reducer#
We are going to create a restaurant order page that allows a diner to select a dish from several options on the menu.
Make sure you have npm installed, then, to create our app, run
npx create-react-app restaurant --template typescript.
Our restaurant app will use React to display a menu to the user. When the user selects an item from the menu, we want to automatically change the state of the app to reflect the user's selection.
To add a reducer, React provides us with a hook called
It works exactly as it sounds: React's
useReducer is a function that allows us to specify the reducer we want to use, and the reducer will go into action to compute the new state of our application!
Here is the repository with the full code for using reducers for React state management with TypeScript. You can also try out the demo of the app on CodeSandbox. The main file to pay attention to is
At the very top of our
App.tsx file, we import React and its
useReducer hook using the line:
To implement our reducer function, we must supply a state object and an action.
The reducer will use this old state object and the dispatched action to figure out what the new state should be. It will then return a new state object for the application to use.
Here is our reducer function that does exactly this:
In this case,
state is the old state we want to replace, and
action is the action fired off from the user interface when a user selects a different menu item.
TypeScript allows us to set types for both the state and action to mitigate against us making type errors in the code.
If you look at the function definition above, you will see that our function arguments are accompanied by types. These types are defined briefly in the file
Using the Reducer to Manage State#
We've defined the reducer function above, but we need to actually assign it as the manager of our application's state.
React provides the method
useReducer(reducer) to enable us to select the reducer to use.
Since our reducer is stored in a function with the name
reducer, we can just assign it as the reducer for React to use with the call
useReducer(reducer). However, when we call
useReducer hook returns a state and a
dispatch method that will fire actions to change the state dynamically.
So we need to store the returned state and the
dispatch method. We do this with the following array destructuring assignment:
useReducer assignment takes place inside our component. Check out the full component code here.
Firing Actions in Response to User Interaction#
Finally, we include logic to fire off actions to the reducer.
These actions will be triggered by the user's interaction with the app. These actions are what lead to state changes in response to the user's selection of different menu items.
Here's the code that fires off an action when the user selects from the available menu items.
What this means is that, if a user selects a "pizza" from the menu, or a "burger", an action will be dispatched with that value as its type. This type is then assessed by the reducer, which takes the action and the old state of the app, then returns a new state for the app.
React will then update the user interface automatically once the state has been changed by our reducer!
And with that, you are ready to dive more into the code.
Of course, if you want to learn how to use reducers with React, as well as testing with TypeScript, common best-practices and patterns, and using SSR with Next - then you should check out Fullstack React with TypeScript: