top of page

How to create your custom saga middleware in Redux

  • Writer: Arthur Sukhinin
    Arthur Sukhinin
  • Apr 7, 2022
  • 3 min read


In this blog I would like to share my experience of creating a middleware Redux-saga in my React project. Redux Saga is a middleware library that allows the Redux store to interact with external resources asynchronously. In this case I would like to use saga for:

  1. Track every action named 'GET_BUDGETS' in my app and redirect that action to my sagas.js.

  2. In the sagas.js I want to call my showLoader action for show my loader while a data is loading, next I would like to call my fetchBudgets method to get all budgets from my Rails api, next to call my FETCH_BUDGETS action for call my budgetsReducer and update my Redux store, next to call my hideLoader action to stop my loader. Of course I would like to track errors in this request.

Ok, let's do it!

First I need to add the Redux-Saga to my React index.js file:

In my app the Redux is already imported:

//...

import { createStore, applyMiddleware } from 'redux'


I need to add:

import createSagaMiddleware from 'redux-saga' // for import saga from 'redux-saga'

const saga = createSagaMiddleware()

const store = createStore( rootReducer, applyMiddleware(thunk, saga)) // for add our saga constant to Redux store.


saga.run(sagaWatcher) // for run our sagaWatcher


Ok, everything looks good here now. Let's move on.

First we need to create our sagas.js file. In sagas.js:

// call, put, and takeEvery are saga effect creator functions. The call() function is used to create effect description, which instructs middleware to call the promise. The put() function creates an effect, which instructs middleware to dispatch an action to the store. The takeEvery() function spawns a saga on each action dispatched to the Store that matches pattern / takeEvery(pattern, saga, ...args) /


With ES6, we have been introduced with a special type of functions called generators. Sagas are implemented as Generator functions (function*) that yield objects to the redux-saga middleware. Inside the generator function we use a yield keyword which is used to pause the function inside. It is a promise which is yielded to the middleware, and the middleware will suspend the Saga until the promise completes.



import { call, put, takeEvery } from 'redux-saga/effects'

import { GET_BUDGETS, FETCH_BUDGETS } from './types'

import { showAlert, showLoader, hideLoader } from './actions'



// The watcher here will watch for dispatched actions and fork a worker on every action. The worker will handle the action.


export function* sagaWatcher() {

yield takeEvery( GET_BUDGETS, sagaWorker )

}


function* sagaWorker() {

try {

yield put(showLoader())

const payload = yield call(fetchBudgets)

yield put ({ type: FETCH_BUDGETS, payload })

yield put(hideLoader())

} catch (e) {

yield put (showAlert('Something go wrong!'))

yield put(hideLoader())

}

}


// and fetchBudgets method for get all budgets from my database.


async function fetchBudgets() {

return await response.json()

}


So, in this case we track every GET_BUDGETS action. When we catch it in our sagas.js sagaWatcher call sagaWorker function. In sagaWorker it start's a showLoader action, next call fetchBudgets function to get all budgets from the database and calling put ({ type: FETCH_BUDGETS, payload }), which instructs the middleware to dispatch an FETCH_BUDGETS action, next and finish our Loader by call hideLoader action. We also trying to catch an error and if we are find it, start our showAlert action with 'Something go wrong!' message.


My budgetReducer:

export const budgetsReducer = (state = [], action) => { debugger; switch(action.type){ case FETCH_BUDGETS: return action.payload ..../ default: return state } };


My fetchBudgets action with GET_BUDGETS request:

export function fetchBudgets() { return { type: GET_BUDGETS } }


Redux-Saga gives us great opportunities for easier to manage, more efficient to execute, easy to test, and better at handling failures.









 
 
 

Comments


  • Facebook
  • Twitter
  • LinkedIn

©2021 by Arthur Sukhinin blog. Proudly created with Wix.com

Subscribe Form

Thanks for submitting!

bottom of page