Skip to content

Commit b75f648

Browse files
committed
[fixed] rendering current handlers before rendering root
when the path changes, the router figures out the next state, but we don't want the current views using it until the root handler is rendered again, allowing people to put up loading indicators, or have live data not break their apps
1 parent 0a01447 commit b75f648

File tree

4 files changed

+74
-12
lines changed

4 files changed

+74
-12
lines changed

examples/async-data/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ var Index = React.createClass({
114114

115115
var routes = (
116116
<Route name="contacts" path="/" handler={App}>
117-
<DefaultRoute handler={Index}/>
117+
<DefaultRoute name="index" handler={Index}/>
118118
<Route name="contact" path="contact/:id" handler={Contact}/>
119119
</Route>
120120
);

modules/__tests__/Router-test.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,6 @@ describe('Router.run', function () {
198198
});
199199
});
200200

201-
describe('RouteHandler', function () {
202-
it('throws if called after the router transitions to a new state');
203-
});
204-
205201
describe('locations', function () {
206202
it('defaults to HashLocation', function (done) {
207203
var routes = <Route path="/" handler={Foo}/>

modules/components/__tests__/RouteHandler-test.js

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,72 @@
11
/** @jsx React.DOM */
2-
32
var assert = require('assert');
43
var expect = require('expect');
54
var React = require('react');
65
var Router = require('../../index');
76
var Route = require('../Route');
87
var RouteHandler = require('../RouteHandler');
8+
var TestLocation = require('../../locations/TestLocation');
9+
var {
10+
Bar,
11+
Foo
12+
} = require('../../__tests__/TestHandlers');
13+
914

1015
describe('RouteHandler', function () {
1116

17+
it('uses the old handler until the top-level component is rendered again', function (done) {
18+
var updateComponentBeforeNextRender;
19+
TestLocation.history = [ '/foo' ];
20+
21+
var Root = React.createClass({
22+
componentDidMount: function () {
23+
updateComponentBeforeNextRender = function (cb) {
24+
this.forceUpdate(cb);
25+
}.bind(this);
26+
},
27+
28+
render: function() {
29+
return (
30+
<div>
31+
<h1>Root</h1>
32+
<RouteHandler/>
33+
</div>
34+
);
35+
}
36+
});
37+
38+
var routes = (
39+
<Route name="root" handler={Root} path='/'>
40+
<Route name="foo" handler={Foo} path='/foo'/>
41+
<Route name="bar" handler={Bar} path='/bar'/>
42+
</Route>
43+
);
44+
45+
var div = document.createElement('div');
46+
var steps = [];
47+
48+
steps.push(function(Handler, state) {
49+
React.render(<Handler/>, div, function () {
50+
expect(div.innerHTML).toMatch(/Foo/);
51+
TestLocation.push('/bar');
52+
});
53+
});
54+
55+
steps.push(function(Handler, state) {
56+
updateComponentBeforeNextRender(function() {
57+
expect(div.innerHTML).toMatch(/Foo/);
58+
React.render(<Handler/>, div, function () {
59+
expect(div.innerHTML).toMatch(/Bar/);
60+
done();
61+
});
62+
});
63+
});
64+
65+
Router.run(routes, TestLocation, function () {
66+
steps.shift().apply(this, arguments);
67+
});
68+
});
69+
1270
it('renders after an update', function (done) {
1371
var Nested = React.createClass({
1472
componentDidMount: function () {

modules/utils/createRouter.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ function createRouter(options) {
138138
var onError = options.onError || defaultErrorHandler;
139139
var onAbort = options.onAbort || defaultAbortHandler;
140140
var state = {};
141+
var nextState = {};
142+
143+
function updateState() {
144+
state = nextState;
145+
nextState = {};
146+
}
141147

142148
// Automatically fall back to full page refreshes in
143149
// browsers that don't support the HTML history API.
@@ -302,11 +308,11 @@ function createRouter(options) {
302308
if (error || transition.isAborted)
303309
return callback.call(router, error, transition);
304310

305-
state.path = path;
306-
state.action = action;
307-
state.routes = nextRoutes;
308-
state.params = nextParams;
309-
state.query = nextQuery;
311+
nextState.path = path;
312+
nextState.action = action;
313+
nextState.routes = nextRoutes;
314+
nextState.params = nextParams;
315+
nextState.query = nextQuery;
310316

311317
callback.call(router, null, transition);
312318
});
@@ -327,7 +333,7 @@ function createRouter(options) {
327333
} else if (transition.isAborted) {
328334
onAbort.call(router, transition.abortReason, location);
329335
} else {
330-
callback.call(router, router, state);
336+
callback.call(router, router, nextState);
331337
}
332338
}
333339

@@ -384,10 +390,12 @@ function createRouter(options) {
384390
},
385391

386392
getInitialState: function () {
393+
updateState();
387394
return state;
388395
},
389396

390397
componentWillReceiveProps: function () {
398+
updateState();
391399
this.setState(state);
392400
},
393401

0 commit comments

Comments
 (0)