Redux and Redux Saga

Learn about the Redux Saga implementation.

What is Redux?

We require a tool through which we can control the state of our apps (data). Redux is a state management tool, and that is what it does. Consider it as a storage space for all the data, which provides access to the entire app data (or, more specifically, its components) to use as needed.

Flow of Redux -

  • Call action from a component (let’s say component A)
  • The action does some work (like API call) then calls the reducer
  • In reducer some state manipulation happens
  • The updated state is stored in the store
  • The component is re-rendered with the updated data

redux.png

Benefits of Redux

  • predictable state - The state in Redux is predictable because reducer functions are pure; therefore, if we pass the same state and the same action, it needs to return the same result. Redux state is also immutable; it can’t be changed or modified.
  • easy to maintain - Considering that it’s predictable and very strict about the structure of the Redux application, anyone who knows Redux will understand it and work with it easily.
  • easy to debug - Redux allows us to log the behavior using available developer tools, makes debugging easier.
  • developer tools available - Redux has a developer tools, that can be used in the browser to see what’s happens in the backend.
  • server-side rendering - Redux supports server-side rendering by allowing to manage initial rendering. Redux sends the state of the application to the server with a response to the server’s request.

Redux Saga

Redux Saga is a middleware that takes over the control of you actions before reaching the reducer directly. So, why do we need middleware in Redux? The data flow between action and reducer works according to a pretty clear pattern, but when we have to communicate with the API or do some other side effect type of action. Middleware helps to perform side effects without blocking the app’s state updates.

Original Working without redux saga:-

Action(s) → Reducer(s)

With Redux saga as middleware:-

Action(s) → Redux Saga →Reducer(s)

Redux-Saga uses ES6 Generators instead of functions. It allows us to easily test, write, and read the asynchronous calls in Redux.

Advantage of Redux Saga

  • no callback hell
  • the actions stay pure, so the asynchronous code is pretty easy to test.
  • Simplicity in organiziting difficult side effects sequences
  • Declarative style

Implementation of Redux Saga

We are taking a example of a simple API call to get the users from a mock API and then consoling the users.

File Structure

src
  -redux
    -userActionTypes.js 
    -userActions.js 
    -userReducer.js
    -store.js
    -saga.js
  -services
    -userRequest.js

Files

Defining constant

// userActionTypes.js 

//variable file
export const GET_USER = "GET_USER";
export const SET_USER = "SET_USER";

Defining actions

It will be called from the component

//userActions.js
import { GET_USER, SET_USER } from "./userActionTypes";

/*these actions are called from the component.
all the actions are attached to unqiue reducers 
*/
export const getUser = () => ({
  type: GET_USER
});

export const setuser = (user) => ({
  type: SET_USER,
  user
});

Defining Reducer

Each reducer is attched to a spefici actions and here the state manipluation is done

//userReducer.js
import { SET_USER } from "./userActionTypes";

const initialUserState = {
  user: undefined
};

export default (state = initialUserState, action) => {
  switch (action.type) {
    case SET_USER:
      const { user } = action;
      return { ...state, user };

    default:
      return state;
  }
};

Setting up a store

After the store is setup remember to provide the store to main <App/> component

import { combineReducers, createStore, applyMiddleware } from "redux";
import userReducer from "./userReducer  ";
import createSagaMiddleware from "redux-saga";
import { watcherSaga } from "./saga";

//combine all the reducers of you app
const reducer = combineReducers({
  user: userReducer
});

// this middle ware enable saga in redux
const sagaMiddleware = createSagaMiddleware();

// created array so that all the middleware can be accessed in a simple way
const middleware = [sagaMiddleware];

// creating redux store with all reducers and middleware
const store = createStore(reducer, {}, applyMiddleware(...middleware));

//setup a listener
sagaMiddleware.run(watcherSaga);

export default store;

Setting up saga

import { takeLatest } from "redux";
import { call, put } from "redux-saga";
import { requestGetUser } from "../services/user";
import { setUser } from "./actions";
import { GET_USER } from "./actionTypes";

//these are generator functions that allow you to do task asynchrnously
function* handleGetuser(action) {
  try {
    //consider it similar to async await
    const response = yield call(requestGetUser);
    const { data } = response;

    //in saga action is dispatched using 'put'
    yield put(setUser(data));
  } catch (error) {
    console.log(error);
  }
}

/*this will be constantly be running in the background and 
  look for any action dispatched by redux store and map it
  to its handler function
 */
export function* watcherSaga() {
  //takeLatest means if two same api are called then it will take the latest and cancel the first once
  yield takeLatest(GET_USER, handleGetuser);
}

Conclusion

Redux saga is a great way to handle async task . Though the whole setup looks complex but it so super easy to use once you get the understanding.