React and TypeScript: `useReducer` Hook
This lesson is a detailed analysis of typing the `useReducer` hook in TypeScript. It also provides a great use case for discriminated unions and state machines.
Overview
In this lesson, we will implement a common data fetching scenario with the useReducer
hook. We will see how to take advantage of TypeScript’s discriminated unions to correctly type reducer’s actions. Finally, we will introduce a useful pattern for representing the state of data fetching operations.
Do we need a reducer?
We will base our code on an example from the official React documentation. The demo linked from this article is a simple implementation of a very common pattern, fetching a list of items from some backend service. In this case, we’re fetching a list of Hacker News article headers.
What functionality is missing in this little demo? When fetching data from the backend it’s useful to indicate to the user that an operation is in progress, and if the operation fails, to show the error to the user. Neither is included in the demo as coded.
The attached code uses useState
hook to store the list of items after it is retrieved. We will need two additional pieces of state to implement our enhancements, a Boolean indicating whether an action is in progress and an optional string containing the error message.
We could use more useState
hooks to store this information. There’s a better way to do this, though. Notice that we’re modifying multiple pieces of state at the same time as a result of certain actions. For example, when data is retrieved from the backend, we update both the data piece and the loading indicator piece. What’s more, we’re modifying the state in multiple places. Wouldn’t it be cleaner and easier to follow if there was only a single place in the component where the state is updated?
We can achieve it by using the useReducer
hook. It will allow us to centralize all state modification, making them easier to track and reason about.
The type of useReducer
Let’s take a look at useReducer
’s type signature. I simplified the code slightly by taking the initializer out of the picture.