Introduction
In the previous article, we have learned about multiple reducer concept in Redux along with its implementation using combineReducer() method. Now in this article, we will be learning about the concept of Middleware along with its usage and Async actions in Redux.
Middleware
In Redux, Middleware provides a third-party extension point between dispatching an action and handling the action off to the reducer
Action <-> Middleware <-> Dispatcher
Middleware provides a way to extend Redux with custom functionality. It is mainly used for Logging, crash reporting, asynchronous requests, route handling and many more.
The best feature of middleware is that it’s composable in a chain. You can use multiple independent third-party middlewares in a single project.
Let’s start with the implementation of Middleware using redux-logger.
So, to implement it by installing it in visual studio code,
npm i --save redux-logger
Now updating previous code as below,
First import redux-logger after installation,
const reduxLogger = require('redux-logger')
Now create a variable for applyMiddleware function from the Redux library,
const applyMiddleware = redux.applyMiddleware
const logger = reduxLogger.createLogger()
After that updating it in createStore as the second argument,
const store = createStore(rootReducer,applyMiddleware(logger))
Now the overall code looks like,
const redux = require('redux')
const reduxLogger = require('redux-logger')
console.log("Index js in redux app")
const createStore = redux.createStore
const combineReducer = redux.combineReducers
const applyMiddleware = redux.applyMiddleware
const logger = reduxLogger.createLogger()
const LOGIN = 'LOGIN'
const USER_DETAIL = 'USER_DETAIL'
// action
function loggedIn(user, pwd) {
return {
type: LOGIN,
username: user,
password: pwd,
loggedInStatus: ""
}
}
function updateUserName(FirstName, LastName, UserName) {
return {
type: USER_DETAIL,
FirstName: FirstName,
LastName: LastName,
UserName: UserName
}
}
function callLoginApi(username, password) {
if (username === 'admin' && password === 'admin') {
return "Login Success";
} else {
return 'Invalid email and password';
}
}
const initialLoginState = {
username: "test",
password: "test",
loggedInStatus: ""
}
const initialUserState = {
FirstName: "",
LastName: "",
UserName: ""
}
const loginReducer = (state = initialLoginState, action) => {
switch (action.type) {
case LOGIN:
return {
...state,
username: action.username,
password: action.password,
loggedInStatus: callLoginApi(action.username, action.password)
}
default:
return state
}
}
const UserReducer = (state = initialUserState, action) => {
switch (action.type) {
case USER_DETAIL:
return {
...state,
FirstName: action.FirstName,
LastName: action.LastName,
UserName: action.UserName
}
default:
return state
}
}
const rootReducer = combineReducer({
login : loginReducer,
userDetail : UserReducer
})
const store = createStore(rootReducer,applyMiddleware(logger))
console.log("Initial State", store.getState())
const unsubscribe = store.subscribe(() => {})
store.dispatch(loggedIn("user", "user"))
store.dispatch(loggedIn("admin", "admin"))
store.dispatch(updateUserName("priyanka", "jain", "prynka.m.jain@gmail.com"))
store.dispatch(updateUserName("test", "test", "test@gmail.com"))
unsubscribe()
This will display the output as below,
As in the image, output is displaying the log that which action is performing after % sign.
Now we are going to learn about Asynchronous action along with middleware.
Async Actions
As in the previous article, we have seen about synchronous action in which as soon as action is dispatched, the state gets updated. But there are some scenarios when we need to update state based on some API calls or something that takes time to execute than we need to use Async Actions.
Let’s look at the demo which fetches data from API using Redux application,
Now for fetching data from API we will see how to write code in Redux application,
First of all, our state should go like,
state = {
loading : true, // display till data load
data : [], // once data loaded it will be displayed in it
error:’’ // While calling API if any error occurs here it will be stored
}
Secondly, Actions need to be performed,
FETCH_DATA_REQUEST – Retrieve a list of data from API
FETCH_DATA_SUCCESS – When data retrieval is done successfully
FETCH_DATA_FAILURE – When there is an error while retrieving data
At last, Reducer will perform an evaluation based on actions,
CASE FETCH_DATA_REQUEST:
loading: true
CASE FETCH_DATA_SUCCESS:
loading: false,
users:data, // data returned from API
CASE FETCH_DATA_FAILURE:
loading : false,
error : error // if any error occurred by API
Now let’s create a new JavaScript file asyncActionDemo.js,
const redux = require('redux')
const createStore = redux.createStore
const initialState = {
loading: false,
data: [],
error: ''
}
const FETCH_DATA_REQUEST = 'FETCH_DATA_REQUEST'
const FETCH_DATA_SUCCESS = 'FETCH_DATA_SUCCESS'
const FETCH_DATA_FAILURE = 'FETCH_DATA_FAILURE'
const fetchDataRequest = () => {
return {
type: FETCH_DATA_REQUEST
}
}
const fetchDataSuccess = data => {
return {
type: FETCH_DATA_SUCCESS,
payload: data
}
}
const fetchDataError = error => {
return {
type: FETCH_DATA_FAILURE,
payload: error
}
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_DATA_REQUEST:
return {
...state,
loading: true
}
case FETCH_DATA_SUCCESS:
return {
loading: false,
data: action.payload,
error:''
}
case FETCH_DATA_FAILURE:
return {
loading: false,
users:[],
error: action.payload
}
}
}
const store = createStore(reducer)
After the basic definition of State, Action, and Reducers now we will need to add API calls for that we will require to install 2 more packages likes
axios
To make a request to API calls
redux-thunk
It is a standard way to define Asynchronous action creators in Redux application. It is a middleware which will be applied to Redux store.
So first we need to install both the packages in terminal using the command
npm install axios redux-thunk
After successful installation of packages,
We will import it in the js file,
const applyMiddleware = redux.applyMiddleware
const thunkMiddleware = require('redux-thunk').default
const axios = require('axios')
and update the middleware in store,
const store = createStore(reducer,applyMiddleware(thunkMiddleware))
Now we will be creating a method which will call API, but as we know in Action creator only actions are returned but using thunk middleware we have the functionality of returning function which is not required to be pure.
In this demo, we will be using dummy API “https://jsonplaceholder.typicode.com”
We will add action creator containing API call,
const fetchData = () => {
return function (dispatch) {
dispatch(fetchDataRequest())
axios.get("https://jsonplaceholder.typicode.com/users")
.then(response => {
const data = response.data.map(user => user.name)
dispatch(fetchDataSuccess(data))
})
.catch(error => {
dispatch(fetchDataError(error.message))
})
}
}
And then dispatch it after subscribing the store,
store.subscribe(() => { console.log(store.getState()) })
store.dispatch(fetchData())
Now the complete code goes like below,
const redux = require('redux')
const createStore = redux.createStore
const applyMiddleware = redux.applyMiddleware
const thunkMiddleware = require('redux-thunk').default
const axios = require('axios')
const initialState = {
loading: false,
data: [],
error: ''
}
const FETCH_DATA_REQUEST = 'FETCH_DATA_REQUEST'
const FETCH_DATA_SUCCESS = 'FETCH_DATA_SUCCESS'
const FETCH_DATA_FAILURE = 'FETCH_DATA_FAILURE'
const fetchDataRequest = () => {
return {
type: FETCH_DATA_REQUEST
}
}
const fetchDataSuccess = data => {
return {
type: FETCH_DATA_SUCCESS,
payload: data
}
}
const fetchDataError = error => {
return {
type: FETCH_DATA_FAILURE,
payload: error
}
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_DATA_REQUEST:
return {
...state,
loading: true
}
case FETCH_DATA_SUCCESS:
return {
loading: false,
data: action.payload,
error: ''
}
case FETCH_DATA_FAILURE:
return {
loading: false,
users: [],
error: action.payload
}
}
}
const fetchData = () => {
return function (dispatch) {
dispatch(fetchDataRequest())
axios.get("https://jsonplaceholder.typicode.com/users")
.then(response => {
const data = response.data.map(user => user.name)
dispatch(fetchDataSuccess(data))
})
.catch(error => {
dispatch(fetchDataError(error.message))
})
}
}
const store = createStore(reducer, applyMiddleware(thunkMiddleware))
store.subscribe(() => { console.log(store.getState()) })
store.dispatch(fetchData())
The output it will display as below,
Now modify the url as incorrect,
const fetchData = () => {
return function (dispatch) {
dispatch(fetchDataRequest())
axios.get("https://jsossnplaceholder.typicode.com/users")
.then(response => {
const data = response.data.map(user => user.name)
dispatch(fetchDataSuccess(data))
})
.catch(error => {
dispatch(fetchDataError(error.message))
})
}
}
The output will now be displayed as,
Summary
In this article, we have learned about the concept of Middleware and how it is implemented in Redux application. We have also learned about Async action, why it is required and how it can be implemented in Redux.
With this article, I’ve finished the concept of Redux. Now, I will move on to the learning of Redux in ReactJS.
Any question or feedback or suggestion, please do comment and let me know.