Skip to Content
Managing Promise Lifecycle Actions
Promise Lifecycle Actions

In a perfect world, every network request we make would yield an immediate and successful response. But network requests can be slow, and sometimes fail. As developers, we need to account for these realities in order to create the best possible experience for our users. If we know a request is pending, we can make our application more user-friendly by displaying a loading state. Similarly, if we know a request has failed, we can display an appropriate error state.

In order to create these satisfying user experiences, we need to keep track of the state our async requests are in at any given moment so that we can reflect those states for the user. It is common to dispatch a “pending” action right before performing an asynchronous operation, and “fulfilled” or “rejected” actions depending on the results of the completed operation. Take this simple thunk action creator, fetchUserById.

import { fetchUser } from './api'; const fetchUserById = (id) => { return async (dispatch, getState) => { const payload = await fetchUser(id); dispatch({type: 'users/addUser', payload: payload}); } }

Rewritten to include pending and rejected actions, it might look like this:

import { fetchUser } from './api' const fetchUserById = (id) => { return async (dispatch, getState) => { dispatch({type: 'users/requestPending'}) try { const payload = await fetchUser(id) dispatch({type: 'users/addUser', payload: payload}) } catch(err) { dispatch({type: 'users/error', payload: err}) } } }

We call these pending/fulfilled/rejected actions promise lifecycle actions. This pattern is so common that Redux Toolkit provides a neat abstraction, createAsyncThunk, for including promise lifecycle actions in your Redux apps. We’ll explore that method in the following exercises.


The diagram shown depicts the lifecycle of a promise: it begins in a pending state and becomes either fulfilled or rejected.

Folder Icon

Take this course for free

Already have an account?