React with the Redux toolkit and CreateAsyncThunk
complementary Guide
In this tutorial, we are going to build an application from scratch using the redux toolkit. We will fetch API data and display it on the screen. The application will also allow users to book rockets and join selected space missions. The whole application is available on github. Live link below
First, let’s create a React application using following the official React docs.
Create react application and test to see if everything works as expected
npx create-react-app app-name
Install the redux and redux toolkit
npm install @reduxjs/toolkit react-redux
Setup Redux Store
Create a new file src/app/store.js
. Import the configureStore
from Redux Toolkit.
- Provide the Redux Store to React.
Update src/index.js
. Replace the code with this. Remember that ReactDOM
has been replaced with createRoot
Create a Redux Slice
The Redux toolkit recommends putting all your application logic inside the src/features
folder. If you want to create a counter feature then your redux logic should be in src/features/counter/counterSlice.js
. For missions the redux logic will be in src/features/mission/missionSlice.js
and so on.
The first feature we want to create is for rockets. So let’s create a filesrc/features/rocket/rocketSlice.js
. Import createSlice
Creating a slice requires a string name to identify the slice, an initial state value, and one or more reducer functions to define how the state can be updated. Once a slice is created, we can export the generated Redux action creators and the reducer function for the whole slice.
The basic structure of a slice look like this.
Let’s first look at what we want to do with this slice. We want to
- Fetch API data
- Render data on UI
- Add reserved status to some of the lists
- List all the data reserved
First, let’s fetch the API data. We will use axios
and createAsyncThunk.
Install Axios and update the rocketSlice.js
npm install axios
You can read more about createAsyncThunk in the official docs.
Remember that we give it a name rockets/fetchRockets
. This will be the action name. It will be referenced in the redux dev tools when we dispatch an action. We will not call it directly in the application.
The official docs recommend that we create the initials state like this
const initialState = {rockets: [],status: 'idle',error: null,};
rockets
will be the array list we are expecting to receive from the API.- Fetching data from an API is a
asynchronous
task.status
track the state of the async action. - If we encounter any errors we put them in the
errors
.
Reducers versus extra reducers
Inside the slice, we have reducers
and extraReducers
. Any difference?
Any async
action will go into the extraReducers
. If not put them in the reducers.
Let’s add this code to the extraReducers
. For details about the extra reducers read the official docs
extraReducers: (builder) => {builder.addCase(fetchRockets.pending, (state) => {state.status = 'loading';}).addCase(fetchRockets.fulfilled, (state, action) => {state.status = 'succeeded';state.rockets = action.payload;}).addCase(fetchRockets.rejected, (state, action) => {state.status = 'failed';state.error = action.error.message;});},
As we’ve discussed earlier, extraReducers
handle Async tasks. It take a builder, which comes with createSlice
. Then for each of the Async task, we want to do something based on the state.
fetchRockets.pending
check if the fetchRockets
status is loading
. If so we set the state of the rockets to loading
.
fetchRockets.fulfilled
Check if the data have been fetched successfully. If so we set the status
to succeeded
. Then we set th state.rocketst
to payload from fetchRockets
fetchRockets.rejected
check if fetchRockets
encountered an error. If so we set the status as failed
.
You can set the status to any name apart from loading, succeeded, or failed. But it should be something relevant because we will reference it later.
The next thing to do is export data from the state. There are different ways to do it. But I will export in the rocketSlice.js
export const selectAllRockets = (state) => state.rockets.rockets;
export const getRocketsStatus = (state) => state.rockets.status;
export const getRocketsError = (state) => state.rockets.error;
Finally the rocketSlice.js
look like this
Add rocketSlice
to store
Update src/App/store.js
use useSelector() to get the data from the redux store
Create a new file src/features/rocket/rocket.js
.Add the following data to the file. This will be the file to handle the UI logic of this feature.
We want to fetch data based on the loading state. Import the rockets in the App.js file
and render it on the screen.
Let’s use redux dev tools to look at what action has been dispatched
If the data is not showing up in redux dev tools kindly go over the steps again. You also need to check your index.js
file. Remember that ReactDom
has been replaced with createRoot
Render data on the screen
let’s import the selectAllRockets
defined in the rocketSlice.js
and render it on the screen. Update the src/features/rocket/rocket.js
file with this
Results
Dispatch Action
What if we want to perform some action and allow the state to respond. Let’s assume a user click a button to reserve some of these rockets.
Currently the API that we have does not have reserved status. So we can do it in the state. Update the function to fetch the data from the API so that each of the list items will have a default reserved status
export const fetchRockets = createAsyncThunk('rockets/fetchRockets',async () => {const response = await axios.get(ROCKET_URL);response.data.forEach((object) => {object.reserved = false;});return response.data;},);
Then we want to keep track of all the reserved rockets. Update the initial state to have an empty list of the reserved list
const initialState = {rockets: [],status: 'idle',error: null,reserved: [],};
Let’s define two actions reserveRocket
to change the reserved state and myReservedRockets
to show the list of rockets reserved.
Remember this does not require an async action. So let’s put them in the reducer. We want to export these two actions from the reducer. Update rocketSlice.js
Let’s now update the rocket.js
file to dispatch an action and handle this action
Results
Profile Feature
What if we want to show all the rockets a user has reserved? I want to do this in a profile feature directory. This can be extended in the future.src/features/profile/profile.js
We need to add a react-router and update the routes. So that we are able to load the data whenever the page loads.
I hope you now understand clearly the flow of actions and data when using the redux toolkit. There is a lot. But the docs are getting too long. The app is available https://github.com/learnwithalfred/react-capstone-101
You can check the other features here, including
- Toggle dark mode using redux
- Missions feature
- React router
I recommend you read the code. You will get an additional understanding of redux-toolkit flow.
Thanks for reading.