From bae97e1c5892cd0870d8c27c2a49bc2dd1a6f3b0 Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Mon, 11 Jan 2016 00:39:16 -0500 Subject: [PATCH] Improve support for server rendering async routes --- modules/Router.js | 10 ++- modules/__tests__/_bc-serverRendering-test.js | 9 +-- modules/__tests__/serverRendering-test.js | 70 +++++++++++++++++-- 3 files changed, 74 insertions(+), 15 deletions(-) diff --git a/modules/Router.js b/modules/Router.js index ab50ca9f38..0642a9abc6 100644 --- a/modules/Router.js +++ b/modules/Router.js @@ -41,12 +41,10 @@ const Router = React.createClass({ }, getInitialState() { - return { - location: null, - routes: null, - params: null, - components: null - } + // Use initial state from renderProps when available, to allow using match + // on client side when doing server-side rendering. + const { location, routes, params, components } = this.props + return { location, routes, params, components } }, handleError(error) { diff --git a/modules/__tests__/_bc-serverRendering-test.js b/modules/__tests__/_bc-serverRendering-test.js index d6dc212895..edab999fa9 100644 --- a/modules/__tests__/_bc-serverRendering-test.js +++ b/modules/__tests__/_bc-serverRendering-test.js @@ -1,9 +1,10 @@ import expect from 'expect' import React, { Component } from 'react' import { renderToString } from 'react-dom/server' -import match from '../match' -import RouterContext from '../RouterContext' + import Link from '../Link' +import match from '../match' +import RoutingContext from '../RoutingContext' describe('v1 server rendering', function () { @@ -68,7 +69,7 @@ describe('v1 server rendering', function () { it('works', function (done) { match({ routes, location: '/dashboard' }, function (error, redirectLocation, renderProps) { const string = renderToString( - + ) expect(string).toMatch(/The Dashboard/) done() @@ -78,7 +79,7 @@ describe('v1 server rendering', function () { it('renders active Links as active', function (done) { match({ routes, location: '/about' }, function (error, redirectLocation, renderProps) { const string = renderToString( - + ) expect(string).toMatch(/about-is-active/) expect(string).toNotMatch(/dashboard-is-active/) diff --git a/modules/__tests__/serverRendering-test.js b/modules/__tests__/serverRendering-test.js index ccf41e31b5..6ccbd29adf 100644 --- a/modules/__tests__/serverRendering-test.js +++ b/modules/__tests__/serverRendering-test.js @@ -1,10 +1,12 @@ import expect, { spyOn } from 'expect' import React, { Component } from 'react' -import { renderToString } from 'react-dom/server' -import match from '../match' +import { renderToStaticMarkup, renderToString } from 'react-dom/server' + import createMemoryHistory from '../createMemoryHistory' -import RouterContext from '../RouterContext' import Link from '../Link' +import match from '../match' +import Router from '../Router' +import RouterContext from '../RouterContext' describe('server rendering', function () { @@ -43,6 +45,16 @@ describe('server rendering', function () { } } + class Async extends Component { + render() { + return ( +
+

Async

+
+ ) + } + } + const DashboardRoute = { path: '/dashboard', component: Dashboard @@ -60,13 +72,20 @@ describe('server rendering', function () { } } + const AsyncRoute = { + path: '/async', + getComponent(location, cb) { + setTimeout(cb(null, Async)) + } + } + const routes = { path: '/', component: App, - childRoutes: [ DashboardRoute, AboutRoute, RedirectRoute ] + childRoutes: [ DashboardRoute, AboutRoute, RedirectRoute, AsyncRoute ] } - it('works', function (done) { + it('works for synchronous route', function (done) { match({ routes, location: '/dashboard' }, function (error, redirectLocation, renderProps) { const string = renderToString( @@ -76,6 +95,16 @@ describe('server rendering', function () { }) }) + it('works for asynchronous route', function (done) { + match({ routes, location: '/async' }, function (error, redirectLocation, renderProps) { + const string = renderToString( + + ) + expect(string).toMatch(/Async/) + done() + }) + }) + it('accepts a custom history', function (done) { const history = createMemoryHistory() const spy = spyOn(history, 'createLocation').andCallThrough() @@ -122,4 +151,35 @@ describe('server rendering', function () { }) }) + describe('server/client consistency', function () { + // Just render to static markup here to avoid having to normalize markup. + + it('should match for synchronous route', function (done) { + match({ routes, location: '/dashboard' }, function (error, redirectLocation, renderProps) { + const serverString = renderToStaticMarkup( + + ) + const browserString = renderToStaticMarkup( + + ) + + expect(browserString).toEqual(serverString) + done() + }) + }) + + it('should match for asynchronous route', function (done) { + match({ routes, location: '/async' }, function (error, redirectLocation, renderProps) { + const serverString = renderToStaticMarkup( + + ) + const browserString = renderToStaticMarkup( + + ) + + expect(browserString).toEqual(serverString) + done() + }) + }) + }) })