createAsyncThunk in Redux-Toolkit

dispatching async actions never looked so good

So this is the third instalment in my Redux-Toolkit series, you can find:

When it comes to managing state in a React application, Redux has become somewhat of an industry standard. Now with the introduction of Redux Toolkit, life has never been easier. Its functional, easy to setup and you get to create slices of your store for better code maintainability and modularity.

Redux at its core is synchronous, so we need to add middleware like Redux-Thunk or Saga to help us with the asynchronous bit. With Redux-Toolkit, we get Thunk already integrated as a dependency.

According to the official docs: createAsyncThunk is a function that accepts a Redux action type string and a callback function that should return a promise. It generates promise lifecycle action types based on the action type prefix that you pass in, and returns a thunk action creator that will run the promise callback and dispatch the lifecycle actions based on the returned promise.

Let’s break it down:

export const fetchToDoList = createAsyncThunk(
"todo/fetchList", async (_, { rejectWithValue },{condition:true}) => {
try {
const list = await getList();
return list;
} catch (err) {
return rejectWithValue([], err);
}
});

createAsyncThunk accepts three parameters:

  1. type: “todo/fetchList”. The general naming convention followed is {reducerName}/{actionType}
  2. payloadCreator: is the callback function (async (_, { rejectWithValue })=>{}), the first param is the argument which is passed to the callback. The second param is the thunkApi(defined below).
  3. options: is an object with two props, condition is a callback which returns a bool that can be used to skip execution, dispatchConditionRejection uses the condition to dispatch the action. If condition is false dispatchConditionRejection will not dispatch any action.

The ThunkApi is important, because you will be depending on the properties defined in it, most of the time. They are:

  1. dispatch: dispatching different actions.
  2. getState: to access the redux store from within the callback
  3. requestId: this is a unique id redux-toolkit generates for each request
  4. signal: this can be used to cancel request.
  5. rejectWithValue: is a utility function that can return to the action creator a defined payload, in case of error.
  6. extra: the “extra argument” given to the thunk middleware on setup, if available

One of the main reasons I prefer using createAsyncThunk, is for the lifecycle actions it provides. The three lifecycle for an action are as follows:

  1. pending: before the the callback is called in the payloadCreator
  2. fulfilled: on successful executions
  3. rejected: in case of an error
[fetchToDoList.fulfilled]: (state, { meta, payload })=> {  
state.todoList = payload;
},
[fetchToDoList.pending]: (state, { meta })=>{
state.loading = "pending";
},
[fetchToDoList.rejected]: (state,{meta,payload,error })=>{
state.error = error;
}

Each lifecycle, is passed the reducer state(no the store obj) and the thunk action creator, which contains the payload(return value) on fulfilled/rejected, meta which contains the requestId and the args passed to the payloadCreator, error in case of rejected.

Let’s look at a simple todoList slice to better understand:

todoSlice

With this we can dispatch our async actions more conveniently, without distributing our logic into multiple files.

You can find the git repo for the todo application here.

A UI /UX Designer and JS Enthusiast, with a passion for travelling and writing.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store