SSR with Next.js, Redux, and Redux-saga part 1

S

SSR in react has always been a hard skill for developers because it lacks standard tools/libraries and best practices. Next.js is trying to provide abstraction over all the setup you need to do for SSR in React while providing enough features and support which otherwise would take a lot of time. While Next.js is undoubtedly solving lot of problems for us. We still need to integrate it with other libraries and tools frequently used. One of the common difficulty is setting up Next.js with redux-saga flow.

In this article, I am going to explain how to perfectly setup Next.js with redux and redux-saga for SSR. Let’s start by creating a new project

INITIAL SETUP

npx create-next-app my-next-app

This is an easy way to start a minimum working next-app. Inside the package.json we can see it installed next, react, and react-dom. Now we need to install redux, redux-saga, etc. Instead of setting up the entire thing manually I am going to use a CLI tool called react-codegen to install and generate the required files and configuration. You can read more about the react-codegen CLI here. Let’s start by installing the CLI

yarn add -g react-codegen

react-codegen requires yarn to be installed. Now once that’s done initiate the setup using:

react-codegen init

This will look into your package.json and install all the required dependencies for our flow. Once everything is installed let’s add a couple of pages by running react-codegen use the example below.

This is going to create a route.js file because this CLI is built to react and doesn’t support next.js yet. So, for this project, we can simply remove the route.js file once we finish setting up pages. If you look at the code you can see that a new component called User is created inside src/User/User.

import React, { Component } from "react"
import { connect } from "react-redux"

export class User extends Component {
  render() {
    return (
      <div>
        <h2>User</h2>
      </div>
    )
  }
}

const mapStateToProps = state => ({
  state: state
})

const mapDispatchToProps = dispatch => ({
  action: () => dispatch({ type: "USER_REQUEST" })
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(User)

REDUX SETUP

We are now going to create redux configurations. To do that we can run react-codegen again and select redux this time. Follow the example below:

When we look at the code, we can see that we have store, redux, middlewares, and saga everything setup. One of the important things we need to take care of here is removing the getComposer function from store.js because this is Next.js and it doesn’t have the window context on the server-side. So the code will be changed to:

import { applyMiddleware, createStore, compose } from "redux";
import middlewares, { sagaMiddleware } from "./middlewares";
import rootReducer from "./reducers";
import saga from "./saga";

const configureStore = initialState => {
  const middlewareEnhancer = applyMiddleware(...middlewares);

  const enhancer = compose(middlewareEnhancer);

  const store = createStore(rootReducer, initialState, enhancer);

  sagaMiddleware.run(saga);

  // Adding hot reloading to redux
  if (process.env.NODE_ENV !== "production" && module.hot) {
    module.hot.accept("./reducers", () => store.replaceReducer(rootReducer));
  }

  return store;
};

export default configureStore;

We can now configure the main _app.js to add the necessary provider. Let’s add a page called _app.js inside pages directory.

import React, { Component } from "react";
import App from "next/app";
import { Provider } from "react-redux";
import configureStore from "../src/store/store";

const store = configureStore();

class App extends Component {
  render() {
    const { Component, pageProps } = this.props;
    return (
      <Provider store={store}>
        <Component {...pageProps} />
      </Provider>
    );
  }
}

export default App;

FIRST PAGE

Now we are ready to create our first page. I am going to use the User view I just created and put that as default export on index.js page for now. Inside pages/index.js replace with:

import User from "../src/views/User/User";

export default User;

To test if the setup is working properly let’s go ahead and modify the User view:

 return (
      <div onClick={this.props.action}>
        <h2>User</h2>
      </div>
    );

I have added an onClick handler to the main div. Now run your project using yarn dev and open your console then click on User you will be able to see the logs on console from saga and redux-logger. Our setup is working perfectly now. I will discuss adding async actions through redux-saga and modifying our setup to work with SSR in the next part.

About the author

Shiva Pandey

Shiva is a Software Engineer with more than 5 years of experience in different industries like banking, services, software development, and startups. His passion is to create solutions that help others to work faster. He is a problem solver and enforces best practices within the team. His career plan is to become a Principal Software Engineer. In his free time, he likes to play music, football, and video games.

1 comment

  • Excellent read, Positive site, where did u
    come up with the information on this posting? I have read a few of the articles on your site now, and
    I love your style.

Recent Posts

Archives

Categories

Shiva Pandey

Get in touch

Shiva is a Software Engineer with more than 5 years of experience in different industries like banking, services, software development, and startups. His passion is to create solutions that help others to work faster. He is a problem solver and enforces best practices within the team. His career plan is to become a Principal Software Engineer. In his free time, he likes to play music, football, and video games.