Skip to content

Commit

Permalink
update redux router engine doc
Browse files Browse the repository at this point in the history
  • Loading branch information
jchip committed Jun 26, 2018
1 parent 8182e72 commit 9953959
Showing 1 changed file with 84 additions and 35 deletions.
119 changes: 84 additions & 35 deletions docs/chapter1/advanced/stand-alone-modules/redux-router-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,52 @@ The Electrode Redux Router Engine is a tool that handles asynchronous data for R

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

### Example Applications
## Table Of Contents

- [Example Applications](#example-applications)
- [Define Your Routes](#define-your-routes)
- [Example Route](#example-route)
- [`init` attribute](#init-attribute)
- [`methods` attribute](#methods-attribute)
- [Routes Dir](#routes-dir)
- [Route Redux Init](#route-redux-init)
- [Route Redux Init Example](#route-redux-init-example)
- [`options` param](#options-param)
- [More Route Redux Init Details](#more-route-redux-init-details)
- [Redux Router Engine](#redux-router-engine-1)
- [Electrode](#electrode)
- [Hapi/Express](#hapiexpress)
- [APIs](#apis)
- [`constructor(options)`](#constructoroptions)
- [`async render(req, options)`](#async-renderreq-options)

## Example Applications

- [Electrode Boilerplate]

- [Express React Redux Webpack Example]

- [Hapi React Redux Example]

## Usage

## Configuration

The [redux-router engine](#redux-router-engine) is initialized by passing a set of [options](#redux-router-engine-api) including [your react router routes](#define-your-routes) definition.

## Define Your Routes

The latest version now uses [react router 4.0](https://www.npmjs.com/package/react-router).

Since [server rendering](https://reacttraining.com/react-router/web/guides/server-rendering) works quite different comparing to how routes are declared for RR4, a common pattern to define a central routes definition by [react-router-config](https://www.npmjs.com/package/react-router-config) is used.
A common pattern to define central routes by [react-router-config](https://www.npmjs.com/package/react-router-config) is used. You can also find more info about RR4's [server rendering here](https://reacttraining.com/react-router/web/guides/server-rendering).

This engine adds these custom props for your route definition to support server side data initialization with [redux].
> **This engine adds these custom props for your route definition to support server side data initialization with [redux].**
- `name` - (optional) A name for the route's redux reducer and store. A name is needed if the route need to supply redux reducer and initial state, and you have multiple routes doing it.
- `name` - (optional) A name for the route's redux reducer and store.
- A name is needed if multiple routes need to supply redux reducer and initial state, or you need a root reducer that can manually manage and propagate state to different route components.
- `init` - (optional) A string that references the JS module to load server side redux data.
- `methods` - (optional) HTTP methods the route allows on the server. This would only make sense for the top level routes.
- `methods` - (optional) HTTP methods the route allows on the server.
- This would only make sense for the top level routes.

Therefore, to configure the engine, you will first need to specify your app's routes according to [react-router-config]'s specs.

### Example Route

Therefore, to configure the engine, you will first need to specify your app's routes according to [react-router-config]'s specs. For example, a typical `routes.jsx` file might look like this:
> **For example, a typical `routes.jsx` file might look like this:**
```js
import Page from "./components/page";
Expand Down Expand Up @@ -74,37 +93,53 @@ const routes = [
export { routes as default };
```

### `init` attribute
#### `init` attribute

- Typically, it should be a string that refers to a Node module to load the redux init function from.
> **Typically, it should be a string that refers to a Node module to load the redux init function from.**
- If it starts with `.` then it will be joined as `{routesDir}/{init}` and then passed to `require`. ie: `require(path.resolve(routesDir, init))`
- More info about [routesDir](#routes-dir)
- else it's directly passed to `require`, which looks up from `node_modules` or from an absolute path that starts with `/`.
- If it starts with `.` then it will be joined as `{routesDir}/{init}` and then passed to `require`.

- Optionally, if it's `true`, then the path from joining `{routesDir}/{route.path}` is `require`d to load the init module.
- ie: `require(path.resolve(routesDir, init))`
- You can see more info about [routesDir here](#routes-dir)

- Otherwise it's directly passed to `require`, which looks up from `node_modules` or from an absolute path that starts with `/`.

> **Optionally, if it's `true`:**
- The path from joining `{routesDir}/{route.path}` is `require`d to load the init module.

- ie: `require(Path.resolve(routesDir, route.path))`
- This means the routes `path` must be a plain path and contains no regex or named params.

### `methods` attribute
#### `methods` attribute

- A string of HTTP methods in lowercase that the route allows. ie: `get`, `get/post`.
- If the attribute is not specified then it's defaulted to `get`.
> **A string of HTTP methods in lowercase that the route allows.**
- ie: `get`, `get/post`.
- Defaulted to `get`.
- Only makes sense for top routes.

### Routes Dir
#### Routes Dir

> **The engine automatically determines a `routesDir` from where to lookup your routes' `init` modules.**
The engine automatically determines a `routesDir` from where to lookup your routes' `init` modules.
- It is `options.routesHandlerPath` if you passed it.

- `routesDir` is `options.routesHandlerPath` if you passed it, with `process.cwd()` prepend if it's not an absolute path.
- with `process.cwd()` prepended if it's not an absolute path.
- ie: `path.resolve(options.routesHandlerPath)`

- else it's determined as `CWD/${APP_SRC_DIR}/server/routes`. ie: `path.resolve(process.env.APP_SRC_DIR || "", "server/routes")`
- Otherwise it's determined as `CWD/${APP_SRC_DIR}/server/routes`.

- ie: `path.resolve(process.env.APP_SRC_DIR || "", "server/routes")`
- where `APP_SRC_DIR` is set by Electrode to point to your app's `src` or `lib`

### Redux Init
### Route Redux Init

Your route's `init` property help the engine locates a JS file that exports a function that return Redux reducer/initialState for the route.

#### Route Redux Init Example

Your route's `init` property help the engine locates a JS file that exports a function:
> **A typical route redux init example:**
```js
export default function reduxInit(options) {
Expand All @@ -126,9 +161,11 @@ export default function reduxInit(options) {
}
```

`options` will contain the following:
##### `options` param

- `req` - the `request` server framework's `request` object.
> **`options` will contain the following:**
- `req` - the framework's `request` object.

- `location` - the URL path from the `req` object

Expand All @@ -140,35 +177,47 @@ export default function reduxInit(options) {

- Note: async results are not awaited yet so they'd be a promise.

Above is a typical example, but here are what you can do:
- `awaitInits` - available to top route only: an async function to wait for async child inits.

- See below for more details.

> **Above is a typical example. You can do more:**
- You can decorate the function with `async` or return a Promise. The engine will wait for it.

- The wait is **concurrent** with other route's async init functions.

The engine will do the following with the init functions:
- Your top level route `init` can return different Redux data (see below).

#### More Route Redux Init Details

> **The engine will do the following with the init functions:**
- It will call all child routes' inits of a top route first, then call the top route's `init`.

- For top route, `options` will contain an async function `awaitInits` that can be `await`ed on to make sure all async child routes are done so the array `inits` contain ready to use result.

- ie: `await options.awaitInits()`

- The reducers from multiple routes will be combined with redux's [combineReducers].

- `initialState`s from multiple routes will be merged into a single new object with `Object.assign`.

The top level route's init can return the following to override the [combineReducers] and merge `initialState` behaviors:
> **The top level route's init can return the following to override the [combineReducers] and merge `initialState` behaviors:**
- If `reducer` is a function, then it's used as the sole reducer when calling redux's [createStore], but `initialState` should still be there for merging with others.

- If the return object contains a single field `store` then it's considered to be a ready made redux store and used directly.

- In both cases you either have to manually process results from your child routes in `options.inits`, or you do every thing in the top route's `init` only.

#### Redux Router Engine
## Redux Router Engine

The [redux-router engine](#redux-router-engine) is initialized by passing a set of [options](#redux-router-engine-api) including [your react router routes](#define-your-routes) definition.

The `ReduxRouterEngine` is stand-alone and can be used in **any** Redux/React application that runs on Express, Hapi or [WalmartLab's Electrode Platform](http://www.electrode.io/). Here's how to configure the engine depending on your framework:

**Electrode**
### 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:

Expand All @@ -186,7 +235,7 @@ module.exports = req => {
};
```

**Hapi/Express**
### 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:

Expand Down

0 comments on commit 9953959

Please sign in to comment.