What Is Thunk In Redux?:
Thunk is used for fetching data from a API and storing response in Redux states which shortens and increases code clean-up.
What did you use to fetch data?
First, by using useEffect hook and in componentDidMount lifecycle, you would have fetched data from an API. What about storing in Redux? You would have used useDispatch hook for storing and then using useSelector for getting the data.
OK? Now, this operations are assigned to Thunk and you don't need to crowd every components in which you use the data you've called an API.
After that, you should check the results for statuses which can be fulfilled, rejected and pending which can be done more easily by using Thunk.
And remember this is a quote from Redux Toolkit documentations:
This abstracts the standard recommended approach for handling async request lifecycles.
Thus, code is cleaner, more standard and more flexible in writing.
Example In Usage
Consider I have a slice called usersSlice.js
. createAsyncThunk
will be used and created as shown below. Assume we want to fetch users list from an API:
import { createAsyncThunk } from '@reduxjs/toolkit';
export const getUsers = createAsyncThunk(
'usersSlice/getUsers',
async () => {
return await fetch('http://localhost:4000').
then(res => res.json());
}
);
const initialState = {
status: null,
data: []
}
const usersSlice = createSlice({
name: 'usersSlice',
initialState,
extraReducers: {
[getUsers.pending] = (state) => {
state.status = 'Pending';
},
[getUsers.fulfilled] = (state, action) => {
state.status = 'Fulfilled';
state.data = action.payload;
},
[getUsers.rejected] = (state) => {
state.status = 'Rejected';
}
}
export default usersSlice.reducer;
First you create a variable called getUsers
which is assigned to createAsyncThunk
(notice export keyword before declaring the variable). createAsyncThunk
has 2 arguments. The first one is a string for specifying the Thunk name and the second one is a async function which will return a promise.
Then you create a slice by using createSlice
. In extraReducers
(notice reducers
property is different) you specify 3 probable states of the promise which are pending
, fulfilled
and rejected
. You decide what should Redux do in these 3 different states of the API.
- Pending means API manipulation is being continued.
- Fulfilled means response was got from API.
- Rejected means API call was failure.
After that, you declare the reducer you've created in configureStore
:
import { configureStore } from '@reduxjs/toolkit';
import usersSlice from './slices/usersSlice';
export const store = configureStore({
reducer: {
usersSlice,
...
}
});
Then, create a component called UsersListComponent.js
and then, you'll do as this:
import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { getUsers } from './store/slices/usersSlice';
...
const dispatch = useDispatch();
const usersData = useSelector(state => state.usersSlice.data);
const usersDataStatus = useSelector(state => state.usersSlice.status);
useEffect(() => {
dispatch(getUsers());
}, []);
First you should dispatch the async function you've created by using createAsyncThunk
. All operations will be done by Redux and BOOM! Everything is ready and you can use useSelector
hook to get data and use it as you like:
return (
<>
{
usersData.map(userData => (
<div>
<span>{userData.id}</span>
<span>{userData.firstName}</span>
<span>{userData.lastName}</span>
</div>
))
}
</>
);
Also you can use status
state for checking the status of the API:
return (
<>
{
usersDataStatus === 'Pending' ? <span>Pending</span> :
usersDataStatus === 'Fulfilled' ? <span>Fulfilled</span> :
usersDataStatus === 'Rejected' ? <span>Rejected</span> :
''
}
{
usersData.map(userData => (
<div>
<span>{userData.id}</span>
<span>{userData.firstName}</span>
<span>{userData.lastName}</span>
</div>
))
}
</>
);
Everything works as before but cleaner, more standard and more flexible.
Congratulation! You've been learning how to use Thunk and createAsyncThunk
in Redux Toolkit.
Top comments (5)
Please note that we do not recommend the object notation of
extraReducers
any more and will deprecate & remove it in the future. Please use the builder notation as shown in redux-toolkit.js.org/api/createSli...Thank you so much for your attention. Actually I was confused which one should be explained. In the future I'll explain more about builder notation in the future.
Thanks again!
Thanks for informing.
Redux toolkit is awesome, but the new syntax is just verbose, these kinds of changes just ruin developer experience and increase complexity.
The new syntax is necessary to support these features with the TypeScript declarations. Even if you are using JavaScript, that means you will get autocomplete in your editor that you could not have before - since the editor will be using TypeScript under the hood to give you that autocomplete.
So these changes are there to give you better developer experience.
I didn't know about it, that would be a big help!
My biggest issue with Redux and its related tools is this kind of changes, so you have to learn both ways to make sure you can handle a simple task.