Steps to add async logic in React + redux app using createAsyncThunk
Redux does everything synchronously , and for that reason we need to use redux middleware ,The Most common async middleware is Redux Thunk
If you know anything about how to use redux-toolkit with React app , Please check this Link before starting this article : Steps to use redux-toolkit with React app
Getting Started
Step 01: Add the status and the error fields to your initial state
const initialState = {
posts: [],
status: 'idle',
error: null ,
}
Step 02: Import the createAsyncThunk
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
Step 03: Create the Thunk using createAsyncThunk
CreateAsyncThunk accepts two arguments :
Prefix for the Action Type
Payload creator callback returns promise ( date or rejection )
const fetchPosts = createAsyncThunk( 'posts/fetchPosts', async () => { try{ const response = await axios(URL)} return [...response.date] }catch(error) { return error.message } )
Step 04: Respond to thunk actions
The async thunk action ( fetch ) is not defined in the slice's reducers, which means the slice reducer can not respond to them, so in this case we add the extraReducer.
The extraReducer accepts a builder param
The builder param lets us define additional reducers for the additional action types.
Each promise status ( action type ) has a reducer, a promise could be :
Pending :
builder.addCase(fetchPosts.pending, (state, action) => { // set status : loading state.status = 'loading' })
Fulfilled
builder.addCase(fetchPosts.fulfilled, (state, action) => { // set status : succeeded state.status = 'succeeded' // set state : data state.posts = action.payload //... })
Rejected
builder.addCase(fetchPosts.rejected, (state, action) => { // set status : failed state.status = 'failed' // set state : data state.error = action.error.message //... })
Global code :
const initialState = {
posts: [],
status: 'idle',
error: null ,
}
const usersSlice = createSlice({
name: 'posts',
initialState,
reducers: {
// standard reducer logic
},
extraReducers: (builder) => {
// Add reducers for additional action types .
/* 1. pending */
builder.
addCase(fetchPosts.pending, (state, action) => {
// set status : loading
state.status = 'loading'
})
/* 2. Fulfilled */
.addCase(fetchPosts.fulfilled, (state, action) => {
// set status : succeeded
state.status = 'succeeded'
// set state : data
state.posts = action.payload
//...
})
/* 3. Rejected */
.addCase(fetchPosts.rejected, (state, action) => {
// set status : failed
state.status = 'failed'
// set state : data
state.error = action.error.message
//...
})
},
})
Step 05: Add the result to the UI
Import the UseSelector , useDispatch , slice reducers and fetch function :
import { useSelector, useDispatch } from 'react-redux' import { /* reducers */ , fetchPosts } from './postsSlice '
read the status and error :
const error = useSelector((state) => state.posts.error) const status = useSelector((state) => state.posts.status)
define the dispatch :
const dispatch = useDispath()
Dispatch the fetch whenever we want :
dispatch(fetchPosts())
Exemple:
useEffect(() => { if( status === 'idle' ){ dispatch(fetchPosts()) } } , [status , dispatch])
THIS IS THE END , THANK YOU FOR READING .