How to handle Redux async call conditionally

How make Redux async Thunk call conditionally.


In Redux Tool Kit [RTK] createThunk used to implement async calls to API. So how do we manage async call in React component ? We can make calls to reducers using dispatch, but it may cause some side effect, so it is better make the call conditionally.

Dispatching conditionally

In the extra reducer section store status state which is initially set to idle.

const initialState: someState = {
    value: [],
    status: 'idle',
};

  extraReducers: builder => {
        builder.addCase(getData.fulfilled, (state, {payload}) => {
            state.value = payload;
            state.status = 'succeeded';
        })
        builder.addCase(getData.pending, (state, {payload}) => {

            state.status = 'loading';
        })
        builder.addCase(getData.rejected, (state, {payload}) => {

            state.status = 'failed';
        })

Here getData is our async Thunk function. When the data fetch is fulfilled the value of status will be succeeded.

We only need to make the async call once, using the useEffect and the status state , can conditionally fire the dispatch.

const status = useAppSelector((state => state.post.status));
 useEffect(() => {
        if (status === 'idle') {
            dispatch(getData());
        }
    }, [status])

When ever refetch is required t, make status state to idle and the dispatch call will be made.

Thunk: Redux Async Fetch

How to implement async call in Redux-toolkit


Redux implements async fetching with the help of Thunk. Thunk is special function which re fetch data and update the state automatically, it is like a magical wheel behind your app.

Create thunk with slice

Redux-toolkit included special function for creating the fetching logic and later implement in the slice.

//userSlice
import {
  createAsyncThunk,
  createSlice,
  PayloadAction,
} from '@reduxjs/toolkit';


interface User {
  id: number;
  email: string;
  name: string;
}
// declaring the types for our state
export type UserState = {
  data: User[];
  pending: boolean;
  error: boolean;
};

const initialState: UserState = {
  data:[],
  pending: false,
  error: false
};

export const getUser = createAsyncThunk('user/getUser', async () => {
  console.log('Thunk invoked');
  const res = await fetch("http://localhost:3000/api/users?limit=100");
  const data =await res.json();
   
  return data.users; 
});
 

export const userSlice = createSlice({
  name: 'user',
  initialState,
  
  extraReducers: builder => {
    builder
      .addCase(getUser.fulfilled, (state, { payload }) => {
      state.data=payload
      })
      .addCase(getUser.pending, (state, { payload }) => {
        state.pending=true
        })    
  }
});

export const selectUser = (state: RootState) =>state.user.data


export default userSlice.reducer;




As you may note, it is an external function, deserve a special special reducer for it. In extra reducer section implement the Thunk.

How to call

useDispatch hook can be used to make call to the getUser async call.

  
import { useAppDispatch, useAppSelector } from "../hooks";
import { selectUser, getUser } from '../fetures/users/userSlice';
....
  const data = useAppSelector(selectUser);
  const dispatch = useAppDispatch();
  dispatch(getUser())

Open the developer tool and watch it refetch data for you.

Setup Redux store in Reactjs


A global store is essential for app when the work base is dramatically growing, or it working a static environment. For vuejs Vuex store fill the gap. For react everybody talk about Reudx.

Redux little bit complicated,reducers,actions,state,store, lots of thing has to be managed. Redux also have another tool called redux-toolkit which is a helper class for managing redux store.

So where to start ?

As the first step install the basic dependencies and react base project (I skip the react part and leave it for you).

# Yarn
yarn add @reduxjs/toolkit

Folder and files needed

Redux didn’t bother about the folder structure of the store, create a folder redux and inside the folder, create store.js and sliceFile.js

Slice

createSlice method of redux-toolkit will automatically create actions and export state for us. Let’s configure reducer, which is the basic functions and a global state for the store. So let’s create the slice

import { createSlice } from "@reduxjs/toolkit"
 

export const hitSlice = createSlice({
  name: "hit",
  initialState: {
    hitCount: 0,
    isWorking: true
  },
  reducers: {
     setHitCount: (state, action) => {
      state.hitCount=action.payload
       )
    }
  },
  extraReducers: builder => {
    
      
  }
}
)

 
export default hitSlice.reducer


So in our slice we had a hitCount state and reducer to set the hits, that’s all.

Configure the Store

Lets import the reducer from the slice we just made, a minimum setup is required.

//redux/store.js
import { configureStore } from "@reduxjs/toolkit";

import hitReducer from "./hitCounter";
 

export default configureStore({
  reducer: { hits: hitReducer, 
      },
});

Our store just finished, and note that we didn’t export the state. In the index.js (in the root of the project) file we have to configure the redux provider and store, so that state will be available for all the components.

import React from 'react';
import ReactDOM from 'react-dom'; 
import App from './App';
import store from './redux/store';
import {Provider} from 'react-redux'
 
 

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
    <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);
 

Accessing the state

In the components we can access the with useSelector hook as follows

import { useSelector} from 'react-redux'
... 
const {hits} = useSelector(state =>state.hits);

Accessing reducers/methods

Remember the setHitCount method we created , we have to use useDispatch hook to invoke the reducer function.

import { useDispatch } from 'react-redux'
import {setHitCounter} from './redux/hitCounter'
..
const dispatch = useDispatch();
dispatch(setHitCounter(3))
const {hits} = useSelector(state =>state.hits);

The hits is reactive in nature, when ever we change the state, all the app components will also get updated.

Async Call

It’s is often require that asynchronously fetching data from an API, redux can perform this using createThunk function. I saved the topic for next post

Stores for React and Nextjs apps

How to implement global Stores in Reactjs apps


State management in bigger application become tedious in Reactjs apps as in other Frameworks. In Vuejs we have a Vuex store which is nice and cool, when come to Nuxtjs, it get simpler.

For react developers there is good library called Redux package, for keeping global state. It is super complicated, I suggest you to use the Redux Toolkit which automate the creation of store

Another way to use super light weight SWR , a cache management solution for your React app.

Here is the official sites

https://swr.vercel.app/

https://swr.vercel.app/

Posts for further reading