Express.js middleware to render a 4r app server-side:
- React UI
- React Router
- Redux state container
- Radium styles
Features
- Drop-in server-side rendering for React+Router+Redux+Radium apps
- Uses React's rock-solid
ReactDOMServer.renderToString
for synchronous rendering - Instant re-hydration of app on browser using Redux initial state
- Set HTML
<head>
content:<title>
&meta
elements with DocumentMeta - Per-component data loading for the current route:
- Not-ok HTTP responses
- 301 for React Router's
<Redirect/>
component - 404 for unmatched URLs
decorateResponse()
with custom status code based on Redux state
- 301 for React Router's
Example Universal Web App
Demonstrates using this renderer in a working universal app.
Install
Add the module to package.json
:
npm install create-render-4r --save # Save peer dependencies for production (otherwise they're only dev dependencies): npm install radium@0.15.x react@0.14.x react-document-meta@2.x react-dom@0.14.x react-redux@4.x react-router@1.x redux@3.x --save
Upgrading
Breaking changes are indicated by major versions. See UPGRADING
Usage
Basic usage in an Express server.js
:
var express = ;var createRender4r = ; var app = ; // These are unique to your own app.var routes = ;var loadStore = ;var layoutHtml = ; // Create the render middleware.var render4r = ; // Add the render for all requests.app; var PORT = processenvPORT || 3000;app;
API
createRender4r()
This function is used to generate the Express.js middleware.
It accepts a single argument, an object:
routes
(Required) The <Router/>
component
loadStore
(Required) A function taking initial state, returning the Redux store; created with Redux createStore
var Redux = ;var createStore = ReduxcreateStore;var combineReducers = ReduxcombineReducers; var reducers = './my-reducers'; { return ;}
layoutHtml
(Required) An HTML template function; this sample uses ES2015 module & template string syntx:
{ return ` <!DOCTYPE html> <html> <head> <script type="application/javascript"> window.__INITIAL_STATE__ = ; </script> </head> <body> <div id="react-view"></div> <script type="application/javascript" src="/bundle.js"></script> </body> </html> `;}
decorateResponse
(Optional) A side-effect function to update the response based on state:
{ /* Example: set 404 response status when the item couldn't be fetched, while the app still renders a nice Not Found message in the UI. */ var errText = stateitem; if errText && /^404/ resstatus404 }
Server-side async data loading
Per-component data loading for the current route.
fetchData()
Define this static (class) method on React components to enable Promise-based server-side fetching. You'll need to use a universal library like isomorphic-fetch within redux-thunk async action creators so they will work equivalently on the server-side & in web browsers.
fetchData(dispatch, props)
dispatch
(required) the Redux store's dispatcherprops
(required) the component's props
Must return a Promise to await completetion of the fetch. (This is what Redux thunk does.)
static { return ;}
sagasToRun()
Define this static (class) method on React components to enable Generator-based server-side fetching via Redux sagas.
sagasToRun(dispatch, props)
dispatch
(required) the Redux store's dispatcherprops
(required) the component's props
Must return an array of arrays of arguments for middleware.run
.
static { return fetchThingsSaga verifyAuthSaga userId: propsparamsauthUserId ;}
Additionally, the Redux store returned by loadStore()
must expose the Saga middleware as store.sagaMiddleware
:
// …;// … { const sagaMiddleware = ; // … // This accessor must be added for Saga middleware to be used server-side: storesagaMiddleware = sagaMiddleware; return store;}
Absolute URLs
Frequently, app code will need its absolute URL, canonical hostname & protocol to render links or make API requests.
This module includes a sourceRequest
reducer to handle this state. It is captured on the server for each request as the Redux store is initialized.
Example state.sourceRequest.host
values:
localhost:3000
example.com
api.example.com:8443
velvet-glacier-1234.herokuapp.com
Example state.sourceRequest.protocol
values:
https
http
To use these values in an app:
- Add this module's reducers to your store:
const rootReducer =
- Then access it in the Redux state:
// `store` is the Redux storeconst state = store;const proto = statesourceRequestprotocol;const host = statesourceRequesthost;