Fed up with Redux boilerplate…? — Part 2
By: Kavindu Vindika
Tags: Node Express, Redux Saga, Axios, React, Redux

Redux — Saga configuration

In the previous part, I have talked about how to avoid boilerplate in redux implementation. But it’s little bit tricky to understand what’s actual purpose of the reduxHelpers as mentioned in the 1st part. And at the same point I have mentioned, states of the global store should be in a similar format to get the best advantage from the reduxHelpers.

So now the question is at what point do we have states in similar kind of format. That happens most of the time when you fetch data from a backend server. When it comes to a commercial web application, the data fetched from backend are in a similar format such as user details, item details if it’s related to an online shopping app, user preferences etc. So now you can realize these data does have some sort of similar format.

Therefore reduxHelpers could be useful to change the states in the global store of the react application whenever the data fetched from the backend. Now making API calls to the backend happens asynchronously. Therefore to watch these asynch functionality of making API calls will be handled by redux-saga configuration.

Before integrating reduxHelper functions as in 1st part with redux-saga configuration, let’s first bootstrap a backend server with node express and mock data to it. (Here data mocking is done because we are not going to create an actual database)

Do the following to move on…

Create an express app to mock data to an endpoint…

Create a node pacakge.json file.

npm init -y

2. Install node express.

npm install express --save

3. Create a dummy database dummy.js in your project folder which is needed to mock data.

const user = {id: 121,name: 'John',age: 23,language: 'English'}; module.exports = user;

4. Create an express app to make http get method to return ‘user’ data to the client application whenever it’s needed and also server will be running on the localhost port 5000. (Create this in app.js file)

const express = require('express');

const dummy = require('./dummy'); // Set up the express app

const app = express(); // get all users

app.get('/user', (req, res) => {

res.status(200).send({

success: 'true',

message: 'user details retrieved successfully',

userDetails: dummy

})

}); const PORT = 5000; app.listen(PORT, () => {

console.log(`server running on port ${PORT}`)

});

5. Now start the server.

node app.js

Now in the terminal you will see the logging of server running on port 5000.

Check localhost:5000/user and you will see the following json object.

mocking data to the backend server running on localhost

Now let’s use saga to watch API calls to the backend and axios, a 3rd party library for data fetching from the backend server.

Do the following in your react app you created in part 1 to integrate saga configuration and axios.

Install axios, redux-saga and redux-logger. With redux-logger actions that are going to be dispatched will be logged with a clear notification of the previous and next state of the global store.

npm install axios redux-saga redux-logger

2. Create a sagas/index.js as follows.

import { takeLatest, put, all } from 'redux-saga/effects';

import { STATE_CHANGE_1 } from '../Actions/actionTypes';

import axios from 'axios'; function* loadUserAsync() { try {

const response = yield axios.get('http://localhost:5000/user');

yield put({ type: STATE_CHANGE_1.ACTIONTYPE_2, payload: response.data });

} catch (error) {

yield put({ type: STATE_CHANGE_1.ACTIONTYPE_3 });

}

} function* watchLoadUser() {

yield takeLatest(STATE_CHANGE_1.ACTIONTYPE_1, loadUserAsync);

} export default function* rootSaga() {

yield all([watchLoadUser()]);

}

3. Change the Store/index.js as follows.

import createSagaMiddleware from 'redux-saga';

import logger from 'redux-logger';

import { createStore, applyMiddleware } from 'redux';

import combinedReducer from '../Reducer';

import rootSaga from '../sagas';

import initialState from './initialState'; const sagaMiddleware = createSagaMiddleware(); const store = createStore(combinedReducer, initialState, applyMiddleware(logger, sagaMiddleware)); sagaMiddleware.run(rootSaga); export default store;

Let’s have a quick explanation for the above code in sagas and Store folders.

When you call actions.loadUserDetails() in the constructor method of the App.js. It will dispatch loadUserDetails action. (Meanwhile in your saga implementation, it watches whether loadUserDetails action is called…keep in your mind that loadUserDetails function is same as the STATE_CHANGE_1.ACTIONTYPE_1)

Once it is called, with the rendering of the App component, saga will execute loadUserAsync() function where actual dispatch occurs. First data will be fetched by using axios and then it will dispatch STATE_CHANGE_1.ACTIONTYPE_2. If any error occurs due to incapability of fetching data from the backend-server, STATE_CHANGE_1.ACTIONTYPE_3 will be dispatched.

5. Now you can run the react app with npm start ,but you won’t be able to fetch the data from the backend server we’ve created. For that browser should give access to the client application which is running on localhost:3000. For that add the following code-snippet in app.js of your express app.

// get all users

app.get('/user', (req, res) => { res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000'); res.status(200).send({

success: 'true',

message: 'user details retrieved successfully',

userDetails: dummy

})

});

6. Now again run your client application and check the following output in browser console.

*** Remove the console log in App.js ***