Redux toolkit is an opinionated, batteries-included toolset for efficient Redux development. I tried redux before and I didn’t like it, you can easily get lost. But the redux toolkit is easy and fast.
What is the redux toolkit?
It is an opinionated, batteries-included toolset for efficient Redux development. Here are the four features of the redux toolkit.
- Simple - Redux toolkit comes with utilities to simplify common use cases such as a store, reducers, and immutable logic.
- Opinionated - It comes with default ways to set up your store, and it includes commonly used redux addons built-in.
- Powerful - You can write mutative immutable update logic and create entire slices of state automatically.
- Effective - Redux toolkit lets you focus on the logic part of your app.
Core redux has some complications that the redux toolkit seeks to solve. Redux has the following concerns:
- Store configuration was too complicated
- Many packages have to be added to do anything meaningful with redux
- Redux needs too much boilerplate code
This post assumes you already know about the redux toolkit and are only interested in createAsyncThunk. So what is createAsyncThunk?.
What createAsyncThunk
createAsyncThunk is a function that accepts an action type and a function that returns a promise, it also generates a thunk that dispatches pending, fulfilled and rejected types based on the promise.
Example
Assume you have a jobs REST API that you want to fetch with your redux toolkit. You could right reducers for start loading, load jobs, and loadingJobsError in your createSlice function as follows.
export const jobsSlice = createSlice({
name: "jobs",
initialState: {
jobsLoading: false,
jobs: [],
failedMessage: "",
},
reducers: {
startJobsLoading: (state) => {
state.jobsLoading = true;
},
loadJobs: (state, action) => {
state.jobs = action.payload;
state.jobsLoading = false;
},
loadingJobsError: (state, action) => {
state.jobsLoading = false;
state.failedMessage: action.payload
},
},
});
export const { startJobsLoading, loadingJobsError, loadJobs } = jobsSlice.actions;
Simplifying with createAsyncThunk
If you want to simplify this code, you can use the createAsyncThunk function to take care of all those reducers. Here is how you can rewrite the above code using the createAsyncThunk.
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { axios } from "axios";
// First, create the thunk
const getJobs = createAsyncThunk(
"jobs/getJobs",
async (thunkAPI) => {
const res = axios.get("https://remoteok.io/api");
return res.data;
}
);
const getJob = createAsyncThunk(
"jobs/getJobs",
async (id, thunkAPI) => {
const res = axios.get(`https://remoteok.io/api/${id}`);
return res.data;
}
);
// Then, handle actions in your reducers:
const jobsSlice = createSlice({
name: "jobs",
initialState: {
jobsLoading: false,
jobs: [],
failedMessage: "",
},
reducers: {},
extraReducers: {
[getJobs.pending]: (state, action) => {
state.jobsLoading = true;
},
[getJobs.fulfilled]: (state, action) => {
state.jobs.push(action.payload);
state.jobsLoading = false;
},
[getJobs.rejected]: (state, action) => {
state.jobsLoading = false;
state.failedMessage: action.payload
},
[
[getJob.fulfilled]: (action) => return action.payload
},
});
createAsyncThunk anatomy
Note: Reducers from the createAsyncThunk are called in the extraReducers field, and NOT reducers. createAsyncThunk accepts three parameters:
- String action type value "jobs/getJobs"
- The payload creator callback function,
- And the options object
type
It's a string used to index additional action types such as pending, fulfilled and rejected, the additional action types represent the lifecycle of the async callback function.
In the above example. we used jobs/getJobs type string, and it will generate the following redux action types:
- pending: jobs/getJobs/pending
- fulfilled: jobs/getJobs/fulfilled
- rejected: jobs/getJobs/rejected
payLoad creator async function
This function returns a promise with the result of async login e.g an axios or fetch function. If this function encounters an error in its lifecycle, it returns a reject promise Error instance or descriptive error message.
payLoadcreator function anatomy
The payload creator function can be called with two arguments:
- arg: a string or object value, for example, if you want to request for individual job posting, you have to pass the id or slug of that particular job in your dispatch. For example in our getJob function, we passed the id argument. Note The arguments can only be a single string, int or object value.
- thunkAPI: this is an object that contains all the Redux thunk function parameters. It also contains:
- dispatch
- getState
- requestId - auto-generated unique string ID value that identifies the request sequence.
- signal - This is a signal object used to identify if another part of the app is requesting a cancellation. It is an AbortCOntroller.signal object
- rejectWithValue(value, [meta]) - a function you can return in your action creator to return a reject response with defined payload and meta.
- fulfillWithValue(value, meta) - a function that can be returned in your action creator to fulfil with a value while having the ability to add to fulfilledAction.meta.
Options object
This is an object with these optional fields:
- condition(arg, {getState, extra}): boolean: It is a callback function you can use to skip execution of the payload creator and all action dispatches.
- dispatchConditionRejection: if condition() returns false, by default actions will not be dispatched. Set this to true if you want to dispatch a rejected action.
- dGenerator(): string - Used to generate a requestId for the request sequence.
- serializeError(error: unknown) => any: This can override the miniSerializeError to define custom serialization logic.
- getPendingMeta({ arg, requestId }, { getState, extra }): any:: This creates an object that will be merged with pendingAction.meta.
What createAsyncThunk returns
The thunk action creator function will have plain action creators for the pending, fulfilled, and rejected cases attached as nested fields. On the getJobs example above, createAsyncThunk will generate the following functions
- getJobs.pending: an action creator that dispatches an 'jobs/getJobs/pending' action
- getJobs.fulfilled: an action creator that dispatches an 'jobs/getJobs/fulfilled' action
- getJobs.rejected: an action creator that dispatches an 'jobs/getJobs/rejected' action