Search code examples
reactjsreact-reduxredux-toolkitredux-thunk

How do I dispatch an action inside setTimeout in createAsyncThunk?


I am learning how to use createAsyncThunk. I have created a simple counter app. It has an add and an subtract button. On clicking those buttons, the counter value with increase or decrease accordingly.

Now I want to add two more buttons which on clicking should do the same increment and decrement but it should be delayed by one second.

I am able to achieve the same when I don't use redux toolkit. This StackBlitz has the code without toolkit

But I am not sure how to approach the same using redux toolkit. Can someone guide me how I should write my createAsyncThunk call and extraReducers to achieve the same?

This Stackblitz contains an example of what I had tried with toolkit

I am not sure how to proceed with my createAsyncThunk call. I tried to dispatch the action inside the createAsync callback, but it did not work.

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

export const asyncAdd = createAsyncThunk('asyncAdd', async (_, thunkAPI) => {


});
const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    count: 0,
    hasError: false,
    isLoading: false,
    users: {},
    todos: {},
  },
  reducers: {
    add: (state, action) => {
      state.count += action.payload;
    },
    sub: (state) => {
      state.count -= 1;
    },
  },
  extraReducers: {
    [asyncAdd.pending]: (state, action) => {
      state.hasError = false;
      state.isLoading = true;
      console.log('PENDING');
    },
    [asyncAdd.fulfilled]: (state, action) => {
      state.hasError = false;
      state.isLoading = false;
      console.log('payload ' + action.payload);
      state.count = action.payload;
      console.log('FULILLED');
    },
    [asyncAdd.rejected]: (state, action) => {
      state.hasError = true;
      state.isLoading = false;
      console.log('REJECTED');
    },
  },
});

export const { add, sub } = counterSlice.actions;
export default counterSlice.reducer;

Solution

  • createAsyncThunk is designed to process some late action using promises, you have to return either a promise or a resolved promise value.

    Use builder object in extraReducers to create pending, rejected and fullfilled cases

    import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
    
    //method one using inbuild await
    export const asyncAdd = createAsyncThunk('aextraReducerssyncAdd', (count) => {
    
      return new Promise((res) => { // return a promise that resolves after one second
        setTimeout(res, 1000, count); 
      });
    });
    
    //method two using await
    export const asyncAdd2 = createAsyncThunk('asyncAdd',async (count) => {
    
      const res = await new Promise((res) => { // return a promise that resolves after one second
        setTimeout(res, 1000, count); 
      });
      return res;
    });
    
    const counterSlice = createSlice({
      name: 'counter',
      initialState: {
        count: 0,
        hasError: false,
        isLoading: false,
        users: {},
        todos: {},
      },
      reducers: {
        add: (state, action) => {
          state.count += action.payload;
        },
        sub: (state) => {
          state.count -= 1;
        },
      },
      extraReducers(builder) { //use builer
        builder.addCase(asyncAdd.fulfilled, function (state, { payload }) {
          state.count += payload;
        }).addCase(asyncAdd.pending,function (state) {
          state.isLoading = true;
        }).addCase(asyncAdd.rejected,function (state) {
          state.hasError = true;
        });
      },
    });
    
    export const { add, sub } = counterSlice.actions;
    export default counterSlice.reducer;
    

    I have forked the stackblitz also