Search code examples
reactjsreduxredux-saga

Redux Saga How to make component re-render after using update, add, delete request


I am using Redux-saga to implement action such as adding user, editing user, deleting user. I want after each action, component will re-render by calling api to get new data (getEmployeeList function). Can u tell me how to do that? Thank u.

This is saga.js file

import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
import axios from 'axios'
import { 
    GET_EMPLOYEE_LIST,
    GET_EMPLOYEE_LIST_SUCCESS,
    DELETE_EMPLOYEE,
    DELETE_EMPLOYEE_SUCCESS,
    EDIT_EMPLOYEE,
    EDIT_EMPLOYEE_SUCCESS
 } from './actions/EmployeeActions'
import ConstantList from '../../app/appConfig'
import { toast } from 'react-toastify'


const API_PATH = ConstantList.API_ENPOINT + "/employees"

export function* getEmployeeList() {
    yield takeEvery(GET_EMPLOYEE_LIST, workEmployeeList)
}

export function* deleteEmployee() {
    yield takeEvery(DELETE_EMPLOYEE, workDeleteEmployee)
}

export function* editEmployee() {
    yield takeEvery(EDIT_EMPLOYEE, workEditEmployee)
}

export function* workEmployeeList() {
    try {
        const url = API_PATH + '/search'
        const response = yield call(axios.post, url, {})

        yield put({
            type: GET_EMPLOYEE_LIST_SUCCESS,
            employeeList: response.data.data
        })

    } catch (error) {
        console.log("Request failed!")
    }
    
}

export function* workDeleteEmployee({id}) {
    try {
        const url = API_PATH + '/' + id
        const response = yield call(axios.delete, url)

        toast.success("Delete Succeed!")
        
        yield put({
            type: DELETE_EMPLOYEE_SUCCESS,
            id
        })
    } catch (error) {
        toast.warning("Delete Failed!")
    }

}

export function* workEditEmployee({ id, employee}) {
    try {
        const url = API_PATH + '/' + id
        const response = yield call(axios.put, url, employee)

        toast.success("Edit Succeed!")

        yield put({
            type: EDIT_EMPLOYEE_SUCCESS,
            id,
            employee
        })
    } catch (error) {
        toast.warning("Edit Failed!")
    }
}

Thi sis EmployeeAction.js file


export const GET_EMPLOYEE_LIST = "GET_EMPLOYEE_LIST"
export const GET_EMPLOYEE_LIST_SUCCESS = 'GET_EMPLOYEE_LIST_SUCCESS'
export const DELETE_EMPLOYEE = 'DELETE_EMPLOYEE'
export const DELETE_EMPLOYEE_SUCCESS = 'DELETE_EMPLOYEE_SUCCESS'
export const EDIT_EMPLOYEE = 'EDIT_EMPLOYEE'
export const EDIT_EMPLOYEE_SUCCESS = 'EDIT_EMPLOYEE_SUCCESS'

export const getEmployeeList = () => ({
    type: GET_EMPLOYEE_LIST
})

export const deleteEmployee = id => ({
    type: DELETE_EMPLOYEE,
    id
})

export const editEmployee = (id, employee) => ({
    type: EDIT_EMPLOYEE,
    id,
    employee
})

This is EmployeeReducer.js

import { toast } from "react-toastify"

import { 
    DELETE_EMPLOYEE_SUCCESS,
    EDIT_EMPLOYEE_SUCCESS,
    GET_EMPLOYEE_LIST_SUCCESS
} from "../actions/EmployeeActions"



const initialState = {}

const EmployeeReducer = (state = initialState, action) => {
    console.log("action - reducer:", action)
    switch (action.type) {
        case GET_EMPLOYEE_LIST_SUCCESS: {
            return {
                ...state,
                data: action.employeeList
            }
        }
        case DELETE_EMPLOYEE_SUCCESS: {
            return {
                ...state,
                data: state.data.filter(item => item.id !== action.id),
                openConfirmationDialog: false
            }

        }
        case EDIT_EMPLOYEE_SUCCESS: {
            const data = state.data
            let dataUpdated = data.filter(item => item.id === action.id)
            const dataNotUpdated = data.filter(item => item.id !== action.id)
            
            // console.log("newData:", filterData)
            // console.log("action-edit:", action)
            return {
                ...state,
                update: state.data.filter(item => item.id === action.id)
            }
        }
        default: 
            return { ...state }
    }
    
}

export default EmployeeReducer

Solution

  • The details will depend on you, but generally there are two approaches how to handle this.

    1. Call the workEmployeeList saga from the other sagas.
    export function* workDeleteEmployee({id}) {
        try {
            const url = API_PATH + '/' + id
            const response = yield call(axios.delete, url)
            yield put({type: DELETE_EMPLOYEE_SUCCESS, id})
    
            yield call(workEmployeeList) // here I call the list saga
        } catch (error) {}
    }
    
    1. Listen to the success actions
    export function* workSuccessWatcher() {
        yield takeEvery([DELETE_EMPLOYEE_SUCCESS, EDIT_EMPLOYEE_SUCCESS], workEmployeeList)
    }
    

    The difference between the two is the dependency direction. In the first solution the workDeleteEmployee depends on workEmployeeList while in the second one workEmployeeList depends on workDeleteEmployee (or in this case the new watcher to be more specific).

    I personally prefer to use the first one when working within the same entity/module/file (like your case) and the second one when dealing with two different entities/modules/files (e.g. when we would want to update list of employee equipment when employees are created/updated/deleted).