Search code examples
reactjsreduxredux-toolkit

How to run async call on a reducer?


import { createSlice, PayloadAction } from "@reduxjs/toolkit"
import { Dispatch } from 'redux';
import axios from "axios"
const API_URL = process.env.REACT_APP_API_HOST_URL || ""

export type projectObj = {
    id?: number
    createdBy?: number,
    title: string,
    description: string,
    endDate: string,
    priority: 'Critical' | 'High' | 'Medium' | 'Low',
    status: 'Not Active' | 'In Progress' | 'Completed',
    progress: number,
    favorite: boolean


}

interface projectState {
    projects: projectObj[],
    projectFetching: boolean
}

const initialState : projectState = {
    projects : [],
    projectFetching: false

}


export const projectSlice = createSlice({
    name: 'projectReducer',
    initialState,
    reducers: {
        /* errors here */
        create: async (state, action : PayloadAction<projectObj>) => {
            const projectObj = action.payload
            state.projects.push(await createProject(projectObj))
        }
    },

})

// CREATE PROJECT

const createProject = async (projectObj : projectObj)  : Promise<projectObj> => {
    try {
        const project : projectObj = await axios.post(`${API_URL}/api/projects`, projectObj)
        return project
    } catch (err : any) {
        return projectObj
    }
}

export const { create } = projectSlice.actions

export default projectSlice.reducer

Create takes in a projectObj with the props list above and my api will create a new project and then return the project object with id in it. I want to push that into my state.

This errors in the create action. The function createProject returns a promise that I need to await on. . What is the proper way to go about this ?

Edit to ask question about answer-

export const projectSlice = createSlice({
    name: 'projectReducer',
    initialState,
    reducers: {
        create: (state, action: PayloadAction<projectObj>) => {
            const projectObj = action.payload
            state.projects.push(projectObj)
        }
    },
})  


export const createProject = (projectObj: projectObj) => async (dispatch: Dispatch) => {
    try {
        const response = await axios.post(`${API_URL}/api/projects`, projectObj)
        const data: projectObj = response.data.project
        dispatch(create(data))
    } catch (err: any) {
        console.log(err)
    }
}

Solution

  • Handle Async Requests with the createAsyncThunk.

    const createProjectThunk = createAsyncThunk(
      "project/createNew",
      async (projectObj: projectObj) => {
        const response = await createProject(projectObj);
        return response;
      }
    );
    
    export const projectSlice = createSlice({
      name: "projectReducer",
      initialState,
      reducers: {
        /* errors here */
      },
      extraReducers: (builder) => {
        // Add reducers for additional action types here, and handle loading state as needed
        builder.addCase(createProjectThunk.fulfilled, (state, action) => {
          // Add user to the state array
          state.projects.push(action.payload);
        });
      }
    });