Multiple Reducers In Redux

Introduction

 
In the previous article, we have learned about the core concepts of Redux, along with their implementation. Now, in this article, we will be learning about various functions provided by the Redux library with their importance and usage.
 

Multiple Reducers

 
Redux has the pattern of a single store principle to maintain one immutable store; however, it also has the concept of multiple reducers.
 
The multiple reducers concept is to handle single functionality per reducer.
 
For example, we can have 2 reducers. One is to manage the user login and other is to update the user details
 
So, I am updating my index.js code as below.
  1. const redux = require('redux')  
  2.   
  3. console.log("Index js in redux app")  
  4. const LOGIN = 'LOGIN'  
  5. const USER_DETAIL = 'USER_DETAIL'  
  6. // action  
  7. function loggedIn(user, pwd) {  
  8.     return {  
  9.         type: LOGIN,  
  10.         username: user,  
  11.         password: pwd,  
  12.         loggedInStatus: ""  
  13.     }  
  14. }  
  15. function updateUserName(FirstName,LastName,UserName){  
  16.     return{  
  17.         type:USER_DETAIL,  
  18.         FirstName : FirstName,  
  19.         LastName:LastName,  
  20.         UserName:UserName  
  21.     }  
  22. }  
  23.   
  24. function callLoginApi(username, password) {  
  25.     if (username === 'admin' && password === 'admin') {  
  26.         return "Login Success";  
  27.     } else {  
  28.         return 'Invalid email and password';  
  29.     }  
  30. }  
  31.   
  32. const initialState = {  
  33.     username: "test",  
  34.     password: "test",  
  35.     loggedInStatus: "",  
  36.     FirstName : "",  
  37.     LastName : "",  
  38.     UserName :""  
  39. }  
  40.   
  41. const reducer = (state = initialState, action) => {  
  42.     switch (action.type) {  
  43.         case LOGIN:  
  44.             return {  
  45.                 ...state,  
  46.                 username: action.username,  
  47.                 password: action.password,  
  48.                 loggedInStatus: callLoginApi(action.username, action.password)  
  49.             }  
  50.         case USER_DETAIL:  
  51.             return {  
  52.                 ...state,  
  53.                 FirstName: action.FirstName,  
  54.                 LastName: action.LastName,  
  55.                 UserName: action.UserName  
  56.             }  
  57.         default:  
  58.             return state  
  59.     }  
  60. }  
  61.   
  62. const store = redux.createStore(reducer)  
  63. console.log("Initial State", store.getState())  
  64. const unsubscribe = store.subscribe(() => console.log('Updated state', store.getState()))  
  65. store.dispatch(loggedIn("user""user"))  
  66. store.dispatch(loggedIn("testing""testing"))  
  67. store.dispatch(loggedIn("admin""admin"))  
  68. store.dispatch(updateUserName("priyanka""jain","[email protected]"))  
  69. store.dispatch(updateUserName("test""test","[email protected]"))  
  70. unsubscribe()   
The output will display as below.
 
 
 
 
As per the above code, we have defined 2 cases in the reducer. Now, we will see it by creating multiple reducers as below,
  1. const redux = require('redux')  
  2.   
  3. console.log("Index js in redux app")  
  4. const LOGIN = 'LOGIN'  
  5. const USER_DETAIL = 'USER_DETAIL'  
  6. // action  
  7. function loggedIn(user, pwd) {  
  8.     return {  
  9.         type: LOGIN,  
  10.         username: user,  
  11.         password: pwd,  
  12.         loggedInStatus: ""  
  13.     }  
  14. }  
  15. function updateUserName(FirstName, LastName, UserName) {  
  16.     return {  
  17.         type: USER_DETAIL,  
  18.         FirstName: FirstName,  
  19.         LastName: LastName,  
  20.         UserName: UserName  
  21.     }  
  22. }  
  23.   
  24. function callLoginApi(username, password) {  
  25.     if (username === 'admin' && password === 'admin') {  
  26.         return "Login Success";  
  27.     } else {  
  28.         return 'Invalid email and password';  
  29.     }  
  30. }  
  31.   
  32. const initialLoginState = {  
  33.     username: "test",  
  34.     password: "test",  
  35.     loggedInStatus: ""  
  36. }  
  37.   
  38. const initialUserState = {  
  39.     FirstName: "",  
  40.     LastName: "",  
  41.     UserName: ""  
  42. }  
  43.   
  44. const loginReducer = (state = initialLoginState, action) => {  
  45.     switch (action.type) {  
  46.         case LOGIN:  
  47.             return {  
  48.                 ...state,  
  49.                 username: action.username,  
  50.                 password: action.password,  
  51.                 loggedInStatus: callLoginApi(action.username, action.password)  
  52.             }  
  53.         default:  
  54.             return state  
  55.     }  
  56. }  
  57.   
  58. const UserReducer = (state = initialUserState, action) => {  
  59.     switch (action.type) {         
  60.         case USER_DETAIL:  
  61.             return {  
  62.                 ...state,  
  63.                 FirstName: action.FirstName,  
  64.                 LastName: action.LastName,  
  65.                 UserName: action.UserName  
  66.             }  
  67.         default:  
  68.             return state  
  69.     }  
  70. }   
Now after updating the code, we have 2 initial states and 2 reducer methods but there is a problem that store accepts only 1 reducer method so to solve that issue we have a function in library named combineReducer(),
 

combineReducer()

 
The combineReducers helper function turns an object whose values are different reducing functions into a single reducing function you can pass to createStore.
 
The result of the reducer calls every child reducer and collects its result into a single state object. The state produced by combineReducer() namespace the state of each reducer under their keys are passed to combine Reducer().
 
Syntax

combineReducer({key1: reducerName1,key2:reducerName2})

A state can be controlled by key names by using different keys for the reducers in the passed objects.
 
For example, for the above code, you can call the following
  1. const rootReducer = combineReducer({  
  2.     login : loginReducer,  
  3.     userDetail : UserReducer  
  4. })  
  5. const store = createStore(rootReducer)   
Now, the final code goes as below.
  1. const redux = require('redux')  
  2.   
  3. console.log("Index js in redux app")  
  4.   
  5. const createStore = redux.createStore  
  6. const combineReducer = redux.combineReducers  
  7.   
  8. const LOGIN = 'LOGIN'  
  9. const USER_DETAIL = 'USER_DETAIL'  
  10. // action  
  11. function loggedIn(user, pwd) {  
  12.     return {  
  13.         type: LOGIN,  
  14.         username: user,  
  15.         password: pwd,  
  16.         loggedInStatus: ""  
  17.     }  
  18. }  
  19. function updateUserName(FirstName, LastName, UserName) {  
  20.     return {  
  21.         type: USER_DETAIL,  
  22.         FirstName: FirstName,  
  23.         LastName: LastName,  
  24.         UserName: UserName  
  25.     }  
  26. }  
  27.   
  28. function callLoginApi(username, password) {  
  29.     if (username === 'admin' && password === 'admin') {  
  30.         return "Login Success";  
  31.     } else {  
  32.         return 'Invalid email and password';  
  33.     }  
  34. }  
  35.   
  36. const initialLoginState = {  
  37.     username: "test",  
  38.     password: "test",  
  39.     loggedInStatus: ""  
  40. }  
  41.   
  42. const initialUserState = {  
  43.     FirstName: "",  
  44.     LastName: "",  
  45.     UserName: ""  
  46. }  
  47.   
  48. const loginReducer = (state = initialLoginState, action) => {  
  49.     switch (action.type) {  
  50.         case LOGIN:  
  51.             return {  
  52.                 ...state,  
  53.                 username: action.username,  
  54.                 password: action.password,  
  55.                 loggedInStatus: callLoginApi(action.username, action.password)  
  56.             }  
  57.         default:  
  58.             return state  
  59.     }  
  60. }  
  61.   
  62. const UserReducer = (state = initialUserState, action) => {  
  63.     switch (action.type) {         
  64.         case USER_DETAIL:  
  65.             return {  
  66.                 ...state,  
  67.                 FirstName: action.FirstName,  
  68.                 LastName: action.LastName,  
  69.                 UserName: action.UserName  
  70.             }  
  71.         default:  
  72.             return state  
  73.     }  
  74. }  
  75.   
  76. const rootReducer = combineReducer({  
  77.     login : loginReducer,  
  78.     userDetail : UserReducer  
  79. })  
  80. const store = createStore(rootReducer)  
  81. console.log("Initial State", store.getState())  
  82. const unsubscribe = store.subscribe(() => console.log('Updated state', store.getState()))  
  83. store.dispatch(loggedIn("user""user"))  
  84. store.dispatch(loggedIn("admin""admin"))  
  85. store.dispatch(updateUserName("priyanka""jain""[email protected]"))  
  86. store.dispatch(updateUserName("test""test""[email protected]"))  
  87. unsubscribe()   
It will display the output as below.
 
 
So, this way, we can implement multiple reducers in Redux. It may be used to maintain AuthReducer, ProfileReducer and so on.
 

Important points while using combineReducer()

 
There are some basic rules that must be followed while using combineReducer() Like,
  1. For any action in the reducer, it must return a state given as a first argument.
  2. It should never return undefined so while writing code should have proper return statement and error should be managed properly else combineReducer will throw it instead of letting the error manifest itself somewhere else. 

Summary

 
In this article, we have learned about the multiple reducers concept and how it can be implemented using the combineReducer() method in Redux. You can download the source code attached along with this article. Now in the next article we will be learning about Middleware in Redux along with Async actions.