Redux Router Engine

Electrode Redux Router Engine is a tool that handles async data for React Server Side Rendering using react-router, Redux, and the Redux Server Rendering pattern.

Module: electrode-redux-router-engine

Install via npm

$ npm install --save electrode-redux-router-engine

Example Applications

Usage

Configuration

The redux-router engine is initialized by passing a set of options including both the your react router routes routes and the initial Redux Store.

Define Your Routes

Therefore, to configure the engine, you will first need specify your app's routes according to react-router's specs. For example, a typical routes.jsx file might look like this:

import { Route, IndexRoute, Redirect } from "react-router";

export default (
  <Route path="/test" component={Page}>
    <IndexRoute component={Home}/>
    <Redirect from="source" to="target" />
    <Route methods="get" path="/subroute" init="subroute-init" component={SubRoute} />
  </Route>
);

For each route, you can add the following optional attributes:

  • init to specify custom redux store initializer for the route
  • methods to specify the HTTP methods you want the route to allow.

init attribute

If true, then uses route's path to load JS file that provides the store initializer.

i.e. <Route path="/subroute" init={true} />, will require file CWD/${options.routesHandlerPath}/subroute.

i.e. <Route path="/subroute" init="custom-init" />, will require file CWD/${options.routesHandlerPath}/custom-init

If undefined then will use the default createReduxStore passed through options.

methods attribute

i.e. <Route methods="get,post" path="/form" component={Form}>

If the attribute is not specified then it's defaulted to "get".

Redux Store

Next, you'll want to configure the redux store using the Redux Async Actions pattern. The first step in this pattern is to handle any middleware you might be using in addition to the required redux thunk middleware which allows actions to return functions instead of objects, an important step that lets synchronous action creators work with networks requests. We're also using redux logger in this example to show how other middlewares can be integrated:

configureStore.js

import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';

const loggerMiddleware = createLogger();

export default function configureStore(initialState) {
  return createStore(rootReducer, initialState, applyMiddleware(
      thunkMiddleware,
      loggerMiddleware
    ));
}

Now let's create the redux store using the configure function from above. This pattern creates the store in a _server-side component _and initializes it with a promise library (the example below uses bluebird):

import configureStore from "./configureStore";
const Promise = require("bluebird");

export default function createReduxStore(req) {
  const store = configureStore();

  return Promise.all([
    store.dispatch(yourAction())
    // dispatch any other asynchronous actions here
  ])
  .then(() => store);
}

For more information about the pattern used here, you can read about using Async Actions in Redux.

Redux Router Engine

The ReduxRouterEngine is created using both the Redux Store and the routes.jsx component, each passed as key/value pairs to an options object. The module is stand-alone and can be used in any Redux application that runs on Express, Hapi or WalmartLab's Electrode framework. Here's how to configure the engine depending on your framework:

Electrode

In an Electrode app, the engine configuration is straightforward: the route handling logic simply returns the output of the engine's render function in the module.exports clause:

import ReduxRouterEngine from "electrode-redux-router-engine";
import { routes } from "../client/routes";
import CreateReduxStore from "./createReduxStore"

module.exports = (req) => {

  if (!req.server.app.routesEngine) {
    req.server.app.routesEngine = new ReduxRouterEngine({ routes, createReduxStore });
  }

  return req.server.app.routesEngine.render(req);
};

Hapi / Express

To configure the Redux Router Engine in an Express or Hapi application, first initialize the engine and then use it within a route handler to return the HTML. An example usage follows:

const ReduxRouterEngine = require("electrode-redux-router-engine");

function createReduxStore(req, match) {
    // this refs to engine

    const store = configureStore();

    return Promise.all([
      store.dispatch(boostrapApp())
      // dispatch any other asynchronous actions here
    ]).then( () => {
      return store;
    });
}

const routes = require("./routes");

const engine = new ReduxRouterEngine({routes, createReduxStore});

// express or Hapi route handler:

function handler(req, res) {
  engine.render(req)
    .then( (result) => {
      // send full HTML with result back using res
    });
}

Note the route handler configuration above is a stub in this case. In a real Hapi or Express application, the route handler would be more complex. You can refer to our express and Hapi example applications for a more specific use case of the engine.render function.

API

constructor(options)

Where options could contain the following fields:

  • routes - required The react-router routes
  • createReduxStore - required async callback that returns a promise resolving to the Redux store
    • It should take (req, match) arguments where match is react-router's match result.
    • If it's a function then its this references the engine instance.
  • withIds - optional boolean to indicate whether to render with react-dataids.
  • stringifyPreloadedState optional callback to return string for the preloaded state
  • logError - optional callback to log any error
    • It should take (req, err) arguments
    • If it's a function then its this references the engine instance
    • Defaulted to console.log
  • renderToString - optional callback to provide custom renderToString
    • It should take (req, store, match, withIds) arguments
    • If desired, it can return a Promise that resolves the HTML string.
  • routesHandlerPath - optional Path to directory to lookup individual route's createReduxStore handlers.
    • This is defaulted to ${process.env.APP_SRC_DIR}/server/routes (for Electrode apps)

engine.render(req, options)

Method to render a route.

  • req - express/Hapi request object
  • options - override options passed to constructor
    • withIds
    • stringifyPreloadedState
    • createReduxStore

If rendering the route is a success, then it returns a promise resolving to:

{
  status: 200,
  html: // string from React renderToString,
  prefetch: // string from stringifyPreloadedState
}

If an error occured, then it returns a promise resolving to:

{
  status: react-router status or 500,
  message: err.message,
  path: // request path,
  _err: // original error object
}

If no route matched, then it returns a promise resolving to:

{
  status: 404,
  message: `router-resolver: Path <path> not found`
}

If react-router found a redirect route, then it returns a promise resolving to:

{
  status: 302,
  path: // redirect location
}

results matching ""

    No results matching ""