aparnajoshi

Redux for server side rendering

Redux: A predictable state container for javascript apps

Redux is a state management system that is used by javascript apps to provide a centralized system for managing applications state. Redux stores the application state in one place and provides access to use and manipulate this state anywhere in the application. Hence, redux maintain a single source of truth using which different parts of the page will be loaded.

In the previous article, we provided a basic setup for routing on the server-side app. Now that we have an app with certain routes set up, the next step would be to add a state management system that can be leveraged across the application.

In this article, we will go through our previous application and add redux to the nodejs react app.

Basic setup:

Let us begin the setup by adding react-router-dom to our project.

npm install --save redux react-redux redux-devtools-extension

Step1: The first step is to create a store and add a preloadedState. The preloadedState is not mandatory, however, if your application requires predefined data, they can be added in the preloadedState. Create a store folder and add the following files.

// src/store/preloadedState.js

export default {
  count: 34
};
// src/store/index.js

import { createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import reducers from "../reducers";
import preloadedState from "./preloadedState";

export default createStore(reducers, preloadedState, composeWithDevTools();

The composeWithDevTools is used to provide redux state access to browser dev-tool-extension. The dev-tool-extension can be used to trace when, where, why, and how your application's state changed.

Step2: Next step is to add actions. Create a new folder to add actions and add the following files.

// src/actions/countAction.js

export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

export const increment = (number) => {
  return { type: INCREMENT, number };
};

export const decrement = (number) => {
  return { type: DECREMENT, number };
};

Actions are the files that perform data transformations, API calls, and then provide this data to the reducers. It is the responsibility of actions to perform any data manipulations required and provide final data to the reducers.

Step3: The next step is to add reducers to our application. Create a new folder for containing reducers and add the following files.

// src/reducers/countReducer.js

import { INCREMENT, DECREMENT } from '../actions/countAction';

const countReducer = (state = {}, action) => {
  switch (action.type) {
    case INCREMENT:
      return action.number ? state + action.number : state + 1;
    case DECREMENT:
      return action.number ? state - action.number : state - 1;
    default:
      return state;
  }
};

export default countReducer;
// src/reducers/index.js

import { combineReducers } from "redux";
import countReducer from "./countReducer";

const reducers = combineReducers({
    count: countReducer
});

export default reducers;

Reducers are the files that contain functions to update data. Reducers are the only place where the state of the application should be updated. Apart from updating the state, reducers should not perform any other operation.

Step4: Now we will plug actions into the Home component and add functions to call them upon button click.

// src/Home.js

import React from 'react';
import { connect } from 'react-redux';
import Nav from './Nav';
import { increment, decrement } from './actions/countAction';

const _Home = (props) => {
  return (
    <div>
      <Nav />
      I am home
      <div>Current number: {props.number}</div>
      <input type="button" value="Increment number" onClick={() => props.incrementNum(20)} />
      <input type="button" value="Decrement number" onClick={() => props.decrementNum(20)} />
    </div>
  );
};

const mapStateToProps = (state) => ({
  number: state.count,
});

const mapDispatchToProps = (dispatch) => ({
  incrementNum: (num) => dispatch(increment(num)),
  decrementNum: (num) => dispatch(decrement(num)),
});

export const Home = connect(mapStateToProps, mapDispatchToProps)(_Home);

Step5: The final step is to wrap both our client-side and server-side app in Redux Provider. Our App.js file will be wrapped with the Redux provider.

import React from 'react';
import { Provider } from 'react-redux';
import Routes from './routes';
import store from './store';

const App = () => {
  return (
    <Provider store={store}>
      <Routes />
    </Provider>
  );
};
export default App;

On visiting localhost, you should be able to see the following screen

localhost

Now, upon the click on the Increment number button, countAction will be dispatched and the number will get incremented by 20. Upon click on the Decrement number button, the number will be decremented by 20. (The number is incremented or decremented by 20, as that's the value we are supplying to the action).

Congratulation, you have successfully added redux to your server-side rendered react application. If you had any difficulties in following this tutorial, please refer to my GitHub repository: https://github.com/AparnaJoshi007/ssr-react-and-node


Aparna Joshi

Written by Aparna Joshi who works as a software engineer in Bangalore. Aparna is also a technology enthusiast, writer, and artist. She has an immense passion and curiosity towards psychology and its implications on human behavior. Her links: Blog, Twitter, Email, Newsletter