Skip to content

Commit 7fc7c67

Browse files
committed
Merge pull request #2883 from taion/client-match-async
Improve support for server rendering async routes
2 parents 1a3dcea + bae97e1 commit 7fc7c67

File tree

3 files changed

+74
-15
lines changed

3 files changed

+74
-15
lines changed

modules/Router.js

+4-6
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,10 @@ const Router = React.createClass({
4141
},
4242

4343
getInitialState() {
44-
return {
45-
location: null,
46-
routes: null,
47-
params: null,
48-
components: null
49-
}
44+
// Use initial state from renderProps when available, to allow using match
45+
// on client side when doing server-side rendering.
46+
const { location, routes, params, components } = this.props
47+
return { location, routes, params, components }
5048
},
5149

5250
handleError(error) {

modules/__tests__/_bc-serverRendering-test.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import expect from 'expect'
22
import React, { Component } from 'react'
33
import { renderToString } from 'react-dom/server'
4-
import match from '../match'
5-
import RouterContext from '../RouterContext'
4+
65
import Link from '../Link'
6+
import match from '../match'
7+
import RoutingContext from '../RoutingContext'
78

89
describe('v1 server rendering', function () {
910

@@ -68,7 +69,7 @@ describe('v1 server rendering', function () {
6869
it('works', function (done) {
6970
match({ routes, location: '/dashboard' }, function (error, redirectLocation, renderProps) {
7071
const string = renderToString(
71-
<RouterContext {...renderProps} />
72+
<RoutingContext {...renderProps} />
7273
)
7374
expect(string).toMatch(/The Dashboard/)
7475
done()
@@ -78,7 +79,7 @@ describe('v1 server rendering', function () {
7879
it('renders active Links as active', function (done) {
7980
match({ routes, location: '/about' }, function (error, redirectLocation, renderProps) {
8081
const string = renderToString(
81-
<RouterContext {...renderProps} />
82+
<RoutingContext {...renderProps} />
8283
)
8384
expect(string).toMatch(/about-is-active/)
8485
expect(string).toNotMatch(/dashboard-is-active/)

modules/__tests__/serverRendering-test.js

+65-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import expect, { spyOn } from 'expect'
22
import React, { Component } from 'react'
3-
import { renderToString } from 'react-dom/server'
4-
import match from '../match'
3+
import { renderToStaticMarkup, renderToString } from 'react-dom/server'
4+
55
import createMemoryHistory from '../createMemoryHistory'
6-
import RouterContext from '../RouterContext'
76
import Link from '../Link'
7+
import match from '../match'
8+
import Router from '../Router'
9+
import RouterContext from '../RouterContext'
810

911
describe('server rendering', function () {
1012

@@ -43,6 +45,16 @@ describe('server rendering', function () {
4345
}
4446
}
4547

48+
class Async extends Component {
49+
render() {
50+
return (
51+
<div className="Async">
52+
<h1>Async</h1>
53+
</div>
54+
)
55+
}
56+
}
57+
4658
const DashboardRoute = {
4759
path: '/dashboard',
4860
component: Dashboard
@@ -60,13 +72,20 @@ describe('server rendering', function () {
6072
}
6173
}
6274

75+
const AsyncRoute = {
76+
path: '/async',
77+
getComponent(location, cb) {
78+
setTimeout(cb(null, Async))
79+
}
80+
}
81+
6382
const routes = {
6483
path: '/',
6584
component: App,
66-
childRoutes: [ DashboardRoute, AboutRoute, RedirectRoute ]
85+
childRoutes: [ DashboardRoute, AboutRoute, RedirectRoute, AsyncRoute ]
6786
}
6887

69-
it('works', function (done) {
88+
it('works for synchronous route', function (done) {
7089
match({ routes, location: '/dashboard' }, function (error, redirectLocation, renderProps) {
7190
const string = renderToString(
7291
<RouterContext {...renderProps} />
@@ -76,6 +95,16 @@ describe('server rendering', function () {
7695
})
7796
})
7897

98+
it('works for asynchronous route', function (done) {
99+
match({ routes, location: '/async' }, function (error, redirectLocation, renderProps) {
100+
const string = renderToString(
101+
<RouterContext {...renderProps} />
102+
)
103+
expect(string).toMatch(/Async/)
104+
done()
105+
})
106+
})
107+
79108
it('accepts a custom history', function (done) {
80109
const history = createMemoryHistory()
81110
const spy = spyOn(history, 'createLocation').andCallThrough()
@@ -122,4 +151,35 @@ describe('server rendering', function () {
122151
})
123152
})
124153

154+
describe('server/client consistency', function () {
155+
// Just render to static markup here to avoid having to normalize markup.
156+
157+
it('should match for synchronous route', function (done) {
158+
match({ routes, location: '/dashboard' }, function (error, redirectLocation, renderProps) {
159+
const serverString = renderToStaticMarkup(
160+
<RouterContext {...renderProps} />
161+
)
162+
const browserString = renderToStaticMarkup(
163+
<Router {...renderProps} history={createMemoryHistory('/dashboard')} />
164+
)
165+
166+
expect(browserString).toEqual(serverString)
167+
done()
168+
})
169+
})
170+
171+
it('should match for asynchronous route', function (done) {
172+
match({ routes, location: '/async' }, function (error, redirectLocation, renderProps) {
173+
const serverString = renderToStaticMarkup(
174+
<RouterContext {...renderProps} />
175+
)
176+
const browserString = renderToStaticMarkup(
177+
<Router {...renderProps} history={createMemoryHistory('/async')} />
178+
)
179+
180+
expect(browserString).toEqual(serverString)
181+
done()
182+
})
183+
})
184+
})
125185
})

0 commit comments

Comments
 (0)