Implement global loading and error state with redux, thunk, routine and TypeScript
A real use case of redux-thunk-routine
When dispatching async actions with
redux-thunk, we usually have three actions for each thunk:
FAILURE. Imagine we have a thunk to fetch data, we would dispatch
FETCH_DATA_REQUEST before we send the API request, then dispatch
FETCH_DATA_SUCCESS when the request succeeds or dispatch
FETCH_DATA_FAILURE when the request fails.
To store the loading state, we probably have a
isFetching in our redux state tree as shown in the redux documentation and we change this state when we receive the
If you have followed this way to implement the loading state, you probably already find that it is really tedious to write the boilerplate code again and again for each thunk.
Luckily, we now have a shiny small library called
redux-thunk-routine (https://www.npmjs.com/package/redux-thunk-routine) to remove those boilerplate code that defines and dispatches actions while having better static type check at the same time (when using TypeScript).
But how about the
isFetching state? Do we still need to write those repetitive code in reducer? Let’s have a look at a more elegant solution and take more benefits from using
redux-thunk-routine — action names are defined in a standard pattern you can safely rely on.
For each routine, the library defines three actions by appending standard suffixes to the routine name. Let’s say we implement “fetch data” thunk using
redux-thunk-routine: it will define three actions for by default:
Assume that all our actions are defined by
redux-thunk-routine, then we can reduce them in a centralized manner to set
isFetching state. Actually, to make the state more flexible, we will declare the loading state as a dictionary-like object — the routine name is the key, and the value(as boolean) indicates whether the routine is loading.
To express it in TypeScript, we write the type definition below:
Then we can write the reducer to catch and handle all actions from the loading state perspective:
To use the state, we have the selectors below:
Note: the selectors above may cause re-render as we are creating selectors dynamically everytime when calling
selectLoading. We should create selectors that accept arguments instead. See: https://github.com/reduxjs/reselect#q-how-do-i-create-a-selector-that-takes-an-argument
As we can see, the design of the loading state and selectors make it super flexible whenever we want to know if
SOME routines are loading.
Amazingly, the same principle also applies to the global error state. This time, let’s have a look at the implementation of all related parts in one file (using ducks):
Thanks for reading :)