Using combined reducers in Redux

Cristina Radulescu
METRO SYSTEMS Romania
3 min readJun 20, 2020

In this article I want to explain how to get the state from Redux using combined reducers. Our goal is to fetch events and user details from the server by dispatching actions and using sagas. We will be using Mockserver to mock server calls.

For fetching the events we will create an actions file, where we will add the actions that we are going to dispatch.

export const FETCH_EVENTS = "FETCH_EVENTS";
export const FINISH_FETCHING_EVENTS = "FINISH_FETCHING_EVENTS";
export const SET_ERROR = "SET_ERROR";
const createAction = (type, data) => ({ type, data });export const startFetchingEvents = () => createAction(FETCH_EVENTS);
export const finishFetchingEvents = (events) => createAction(FINISH_FETCHING_EVENTS, events);
export const setError = (error) => createAction(SET_ERROR, error);

After that we need to create the associated reducer which will set the state in Redux based on the action type which is dispatched:

export const initialState = {
loadingState: null,
events: [],
error: null,
};
export const eventReducer = (state = initialState, action) => { switch (action.type) {
case FETCH_EVENTS:
return {...state, loadingState: "LOADING"};
case FINISH_FETCHING_EVENTS:
return {...state, events: action.data, loadingState: "LOADED"};
case SET_ERROR:
return {...state, error: action.data, loadingState: "ERROR"};
}
return state;
};

We are going to create an actions file for the actions that are going to be dispatched for fetching user details:

export const FETCH_USER_DETAILS = "FETCH_USER_DETAILS";
export const FINISH_FETCHING_USER_DETAILS = "FINISH_FETCHING_USER_DETAILS";
export const SET_USER_ERROR = "SET_USER_ERROR";
const createAction = (type, data) => ({ type, data });export const startFetchingUserDetails = () => createAction(FETCH_USER_DETAILS);export const finishFetchingUserDetails = (user) => createAction(FINISH_FETCHING_USER_DETAILS, user);export const setUserError = (error) => createAction(SET_USER_ERROR, error);

We are going to create the associated reducer for setting the user state in Redux:

export const initialState = {
loadingState: null,
user: null,
error: null,
};
export const userReducer = (state = initialState, action) => { switch (action.type) {
case FETCH_USER_DETAILS:
return {...state, loadingState: "LOADING"};
case FINISH_FETCHING_USER_DETAILS:
return {...state, user: action.data, loadingState: "LOADED"};
case SET_USER_ERROR:
return {...state, error: action.data, loadingState: "ERROR"};
}
return state;
};

We need to create the root reducer where we combine both reducers that were previously created. We need to pay attention to the naming we use in the combineReducers method and how we import our reducers. This basically defines the path that we are going to use for getting the state from Redux.

import {combineReducers} from "redux";
import {eventReducer as eventDetails} from "./eventReducer";
import {userReducer as userDetails} from "./userReducer";
export const rootReducer = combineReducers({
eventDetails,
userDetails,
});

We are going to retrieve the state from Redux in the container, where we are going to map the state to props and dispatch the actions defined to props. In order to take the state from Redux, you need to pay attention to how the reducers were mapped in the root reducers and the naming that was used there. The state and actions dispatched can be viewed in the Redux Dev Tools plugin in Google Chrome.

export const mapStateToProps = (state) => ({
events: state.eventDetails.events,
user: state.userDetails.user,
});
export const mapDispatchToProps = (dispatch) => ({
fetchAllEvents: () => dispatch(startFetchingEvents()),
fetchUserDetails: () => dispatch(startFetchingUserDetails()),
});
export default connect(mapStateToProps, mapDispatchToProps)(AllEvents);

An action that is being dispatched can trigger a saga. When the startFetchingEvents() action is dispatched the saga will be triggered based on the type of the action, in this case FETCH_EVENTS ;

export function* fetchAllEventsEffect() {
try {
const response = yield call(fetchEvents);
yield put(finishFetchingEvents(response.data));
} catch (e) {
console.error("Could not load events", e);
yield put(setError(e));
}
}
export default function* fetchAllEvents() {
yield takeLatest(FETCH_EVENTS, fetchAllEventsEffect);
}export function fetchEvents() {
return axios.get('http://localhost:8080/allevents');
}

The source code for this project can be found here.

After reading this article I hope you will have a better understanding of how combined reducers work.

Please let me know in the comments section below what you think. Thanks.😁

--

--