Skip to content
This repository has been archived by the owner on Jan 2, 2018. It is now read-only.

Commit

Permalink
Support async routes with SSR (#43)
Browse files Browse the repository at this point in the history
* Supports async routes by utilizing `match` for the initial client render if server-side rendering is enabled

* Uses `require` provided by `require.ensure`

* Renames parameters from `props` to `options`

* fix(client): Always render asynchronously to the DOM to allow code splitting

* style(lint): Use proper JSON formatting
  • Loading branch information
PAkerstrand authored and dlmr committed Oct 23, 2017
1 parent 0ff7baa commit 5c7db39
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 21 deletions.
1 change: 1 addition & 0 deletions examples/complex/src/components/app/index.js
Expand Up @@ -20,6 +20,7 @@ export default class App extends Component {
<li><Link to="/about/">About</Link></li>
<li><Link to={{ pathname: '/long/', state: { scrollToTop: true } }}>Long</Link></li>
<li><Link to="/simple/">Simple</Link></li>
<li><Link to="/async/">Async</Link></li>
</ul>
</nav>
{ this.props.children }
Expand Down
11 changes: 11 additions & 0 deletions examples/complex/src/components/async/index.js
@@ -0,0 +1,11 @@
import React, { Component } from 'react';

export default class Async extends Component {
render() {
return (
<div>
Such asynchronously rendered!
</div>
);
}
}
13 changes: 13 additions & 0 deletions examples/complex/src/routes.js
Expand Up @@ -14,6 +14,19 @@ export default () => (
<Route path="about/" component={ About } data={2} ignoreScrollBehavior />
<Route path="long/" component={ Long } />
<Route path="simple/" component={ Simple } data={3} />
<Route
path="async"
getComponent={ (nextState, callback) => {
require.ensure([], (require) => {
const Async = require('./components/async').default;
callback(
null,
Async
);
});
}}
data={4}
/>
</Route>
);

Expand Down
4 changes: 2 additions & 2 deletions extensions/roc-package-web-app-react-dev/.eslintrc
Expand Up @@ -3,15 +3,15 @@

"parser": "babel-eslint",

plugins: ["eslint-plugin-babel"],
"plugins": ["eslint-plugin-babel"],

"rules": {
"indent": [2, 4, { "SwitchCase": 1 }],
"max-len": [2, 120, 4],
"no-warning-comments": 1,

"generator-star-spacing": 0,
"babel/generator-star-spacing": [2, { before: false, after: true }],
"babel/generator-star-spacing": [2, { "before": false, "after": true }],

"import/order": [2, { "groups": ["builtin", "external", "internal", "parent", "sibling", "index"], "newlines-between": "always"}],
"import/newline-after-import": [2],
Expand Down
4 changes: 2 additions & 2 deletions extensions/roc-package-web-app-react/.eslintrc
Expand Up @@ -3,7 +3,7 @@

"parser": "babel-eslint",

plugins: ["eslint-plugin-babel"],
"plugins": ["eslint-plugin-babel"],

"rules": {
"indent": [2, 4, { "SwitchCase": 1 }],
Expand All @@ -13,7 +13,7 @@
"arrow-parens": ["off", "as-needed"],

"generator-star-spacing": 0,
"babel/generator-star-spacing": [2, { before: false, after: true }],
"babel/generator-star-spacing": [2, { "before": false, "after": true }],

"import/order": [2, { "groups": ["builtin", "external", "internal", "parent", "sibling", "index"], "newlines-between": "always"}],
"import/newline-after-import": [2],
Expand Down
29 changes: 12 additions & 17 deletions extensions/roc-package-web-app-react/app/client/create-client.js
Expand Up @@ -3,7 +3,6 @@
/* eslint-disable global-require */
import React from 'react';
import ReactDOM from 'react-dom';
import Router from 'react-router/lib/Router';
import useRouterHistory from 'react-router/lib/useRouterHistory';
import applyRouterMiddleware from 'react-router/lib/applyRouterMiddleware';
import createHistory from 'history/lib/createBrowserHistory';
Expand All @@ -13,6 +12,8 @@ import { useRedial } from 'react-router-redial';

import { rocConfig } from '../shared/universal-config';

import renderToDOM from './render-to-dom';

const clientDebug = debug('roc:client');

const basename = ROC_PATH === '/' ? '' : ROC_PATH;
Expand Down Expand Up @@ -81,8 +82,6 @@ export default function createClient({
}

const render = () => {
const node = document.getElementById(mountNode);

const forceRefreshSetting = rocConfig.runtime.history.forceRefresh;
let history = useRouterHistory(createHistory)({
basename,
Expand All @@ -91,11 +90,6 @@ export default function createClient({
: forceRefreshSetting,
});

let initialLoading;
if (HAS_CLIENT_LOADING) {
initialLoading = require(ROC_CLIENT_LOADING).default;
}

let routes;
let locals = {
history,
Expand Down Expand Up @@ -168,13 +162,14 @@ export default function createClient({
));
}

const node = document.getElementById(mountNode);
let updateScroll = () => {};

const middlewares = [
useRedial({
...routerMiddlewareConfig['react-router-redial'],
locals,
initialLoading,
initialLoading: HAS_CLIENT_LOADING ? require(ROC_CLIENT_LOADING).default : undefined,
beforeTransition: rocConfig.runtime.fetch.client.beforeTransition,
afterTransition: rocConfig.runtime.fetch.client.afterTransition,
parallel: rocConfig.runtime.fetch.client.parallel,
Expand All @@ -201,16 +196,16 @@ export default function createClient({
);
}

const finalComponent = compose(createComponent)(
<Router
history={history}
routes={routes}
render={applyRouterMiddleware(...middlewares)}
/>
renderToDOM(
{
createComponent: compose(createComponent),
history,
routes,
routerRenderFn: applyRouterMiddleware(...middlewares),
},
node
);

ReactDOM.render(finalComponent, node);

if (__DEV__) {
const devNode = document.createElement('div');
node.parentNode.insertBefore(devNode, node.nextSibling);
Expand Down
25 changes: 25 additions & 0 deletions extensions/roc-package-web-app-react/app/client/render-to-dom.js
@@ -0,0 +1,25 @@
/* eslint-disable global-require */
import React from 'react';
import ReactDOM from 'react-dom';
import Router from 'react-router/lib/Router';
import match from 'react-router/lib/match';

function renderSync({ renderProps, createComponent, routerRenderFn }, node) {
const finalComponent = createComponent(
<Router
{...renderProps}
render={routerRenderFn}
/>
);

ReactDOM.render(finalComponent, node);
}

export default function renderAsync({ history, routes, ...rest }, node) {
match({ history, routes }, (error, redirectLocation, renderProps) => {
renderSync({
...rest,
renderProps,
}, node);
});
}

0 comments on commit 5c7db39

Please sign in to comment.