Skip to content

Commit e4f3446

Browse files
committed
Callback update: fire navigation callbacks on all routers with unique match objects.
Note arity change; callbacks are now (path, navigation, match) from (path, navigation). `navigation` no longer has a `match` attribute. Fixes #95.
1 parent 2c18154 commit e4f3446

File tree

4 files changed

+120
-32
lines changed

4 files changed

+120
-32
lines changed

lib/CaptureClicks.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,10 @@ var CaptureClicks = React.createClass({
9898
// flag if we already found a "not found" case and bailed
9999
var bail = false;
100100

101-
var onBeforeNavigation = function(path, navigation) {
101+
var onBeforeNavigation = function(path, navigation, match) {
102102
if (bail) {
103103
return false;
104-
} else if (!navigation.match || !navigation.match.match) {
104+
} else if (!match || !match.match) {
105105
bail = true;
106106
this.props.gotoURL(el.href);
107107
return false;

lib/RouterMixin.js

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ var RouterMixin = {
5050
var parent = props.contextual && this.getParentRouter();
5151

5252
if (parent) {
53-
54-
var parentMatch = parent.getMatch();
53+
// Build our new path based off the parent. A navigation may be in progress, in which case
54+
// we as well want the newest data so we use the pending match.
55+
var parentMatch = parent._pendingMatch || parent.getMatch();
5556

5657
invariant(
5758
props.path ||
@@ -86,7 +87,8 @@ var RouterMixin = {
8687
matchProps: match.getProps(),
8788
handler: match.getHandler(),
8889
prefix: prefix,
89-
navigation: {}
90+
navigation: {},
91+
path: path
9092
};
9193
},
9294

@@ -165,31 +167,22 @@ var RouterMixin = {
165167
* @param {Callback} cb
166168
*/
167169
setPath: function(path, navigation, cb) {
168-
var match = matchRoutes(this.getRoutes(this.props), path, this.getURLPatternOptions());
169-
170-
var state = {
171-
match: match,
172-
matchProps: match.getProps(),
173-
handler: match.getHandler(),
174-
prefix: this.state.prefix,
175-
navigation: navigation
176-
};
177-
178-
assign(navigation, {match: match});
170+
var state = this.getRouterState(this.props);
171+
state.navigation = navigation;
179172

180173
if (this.props.onBeforeNavigation &&
181-
this.props.onBeforeNavigation(path, navigation) === false) {
174+
this.props.onBeforeNavigation(state.path, navigation, state.match) === false) {
182175
return;
183176
}
184177

185178
if (navigation.onBeforeNavigation &&
186-
navigation.onBeforeNavigation(path, navigation) === false) {
179+
navigation.onBeforeNavigation(state.path, navigation, state.match) === false) {
187180
return;
188181
}
189182

190183
this.delegateSetRoutingState(state, function() {
191184
if (this.props.onNavigation) {
192-
this.props.onNavigation(path, navigation);
185+
this.props.onNavigation(state.path, navigation, state.match);
193186
}
194187
cb();
195188
}.bind(this));
@@ -207,6 +200,9 @@ var RouterMixin = {
207200
* by router itself) or use replaceState.
208201
*/
209202
delegateSetRoutingState: function(state, cb) {
203+
// Store this here so it can be accessed by child contextual routers in onBeforeNavigation.
204+
this._pendingMatch = state.match;
205+
210206
if (this.setRoutingState) {
211207
this.setRoutingState(state, cb);
212208
} else {

lib/environment/Environment.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,7 @@ Environment.prototype.register = function register(router) {
7676
this.start();
7777
}
7878

79-
if (router.getParentRouter === undefined || !router.getParentRouter()) {
80-
this.routers.push(router);
81-
}
79+
this.routers.push(router);
8280
};
8381

8482
/**

tests/browser/browser.js

Lines changed: 104 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict';
2-
var assert = require('power-assert');
2+
var assert = require('assert');
3+
var assign = Object.assign || require('object-assign');
34
var React = require('react');
45
var ReactDOM = require('react-dom');
56
var ReactTestUtils = require('react/lib/ReactTestUtils');
@@ -230,15 +231,19 @@ describe('Routing', function() {
230231
});
231232

232233
describe('Navigation lifecycle callbacks', function () {
233-
it('calls onBeforeNaviation and onNavigation', function(done) {
234+
it('calls onBeforeNavigation and onNavigation', function(done) {
234235
assertRendered('mainpage');
235236
var called = [];
236237
app.setState({
237-
beforeNavigationHandler: function (nextPath) {
238+
beforeNavigationHandler: function (nextPath, navigation, match) {
238239
called.push(nextPath);
240+
assert.equal(match.match.slug, 'hello');
241+
assert(!navigation.match);
239242
},
240-
navigationHandler: function (path) {
243+
navigationHandler: function (path, navigation, match) {
241244
called.push(path);
245+
assert.equal(match.match.slug, 'hello');
246+
assert(!navigation.match);
242247
}
243248
});
244249
router.navigate('/__zuul/hello', function () {
@@ -390,7 +395,7 @@ describe('Nested routers', function() {
390395
var NestedRouter = React.createClass({
391396
render: function() {
392397
return div(null,
393-
Locations(null,
398+
Locations(this.props,
394399
Location({
395400
path: '/__zuul/nested/',
396401
handler: div(null, 'nested/root')
@@ -405,9 +410,17 @@ describe('Nested routers', function() {
405410

406411
var App = React.createClass({
407412

413+
getInitialState: function() {
414+
return {};
415+
},
416+
408417
render: function() {
409418
return div(null,
410-
Locations({ref: 'router', className: 'App'},
419+
Locations({
420+
ref: 'router', className: 'App',
421+
onNavigation: this.state.navigationHandler,
422+
onBeforeNavigation: this.state.beforeNavigationHandler
423+
},
411424
Location({
412425
path: '/__zuul',
413426
foo: 'bar',
@@ -420,7 +433,9 @@ describe('Nested routers', function() {
420433
}),
421434
Location({
422435
path: '/__zuul/nested/*',
423-
handler: NestedRouter
436+
handler: NestedRouter,
437+
onNavigation: this.state.navigationHandler,
438+
onBeforeNavigation: this.state.beforeNavigationHandler
424439
})
425440
),
426441
CaptureClicks({gotoURL: this.gotoURL},
@@ -457,6 +472,42 @@ describe('Nested routers', function() {
457472
});
458473
});
459474

475+
it('calls onBeforeNaviation and onNavigation with correct match objects w/ multiple routers', function(done) {
476+
assertRendered('mainpage');
477+
var called = [];
478+
router.navigate('/__zuul/nested/');
479+
// Goes beforeNav, beforeNav, nav, nav
480+
app.setState({
481+
beforeNavigationHandler: function (nextPath, navigation, match) {
482+
called.push(nextPath);
483+
if (called.length === 1) {
484+
assert.equal(match.match._[0], 'page');
485+
assert.equal(match.matchedPath, '/__zuul/nested/');
486+
} else {
487+
assert.equal(match.matchedPath, '/__zuul/nested/page');
488+
}
489+
assert(!navigation.match);
490+
},
491+
navigationHandler: function (path, navigation, match) {
492+
called.push(path);
493+
if (called.length === 3) {
494+
assert.equal(match.match._[0], 'page');
495+
assert.equal(match.matchedPath, '/__zuul/nested/');
496+
} else {
497+
assert.equal(match.matchedPath, '/__zuul/nested/page');
498+
}
499+
assert(!navigation.match);
500+
}
501+
});
502+
router.navigate('/__zuul/nested/page', function () {
503+
assert.equal(called.length, 4);
504+
called.forEach(function(c) {
505+
assert.equal(c, '/__zuul/nested/page');
506+
});
507+
done();
508+
});
509+
});
510+
460511
describe('CaptureClicks component', function() {
461512
it('navigates to a subroute via onClick event', function(done) {
462513
assertRendered('mainpage');
@@ -498,7 +549,7 @@ describe('Contextual routers', function() {
498549

499550
render: function() {
500551
return div(null,
501-
Locations({ref: 'router', contextual: true},
552+
Locations(assign({ref: 'router', contextual: true}, this.props),
502553
Location({
503554
path: '/',
504555
handler: div(null, 'subcat/root')
@@ -519,16 +570,26 @@ describe('Contextual routers', function() {
519570

520571
var App = React.createClass({
521572

573+
getInitialState: function() {
574+
return {};
575+
},
576+
522577
render: function() {
523-
return Locations({ref: 'router'},
578+
return Locations({
579+
ref: 'router',
580+
onNavigation: this.state.navigationHandler,
581+
onBeforeNavigation: this.state.beforeNavigationHandler
582+
},
524583
Location({
525584
path: '/__zuul',
526585
handler: div(null, "mainpage")
527586
}),
528587
Location({
529588
path: '/__zuul/subcat/*',
530589
handler: SubCat,
531-
ref: 'subcat'
590+
ref: 'subcat',
591+
onNavigation: this.state.navigationHandler,
592+
onBeforeNavigation: this.state.beforeNavigationHandler
532593
})
533594
);
534595
}
@@ -587,6 +648,39 @@ describe('Contextual routers', function() {
587648
});
588649
});
589650
});
651+
652+
it('calls onBeforeNaviation and onNavigation with correct match objects w/ contextual routers', function(done) {
653+
assertRendered('mainpage');
654+
var called = [];
655+
router.navigate('/__zuul/subcat/');
656+
// Goes beforeNav, beforeNav, nav, nav
657+
app.setState({
658+
beforeNavigationHandler: function (nextPath, navigation, match) {
659+
called.push(nextPath);
660+
if (called.length === 1) {
661+
assert.equal(match.match._[0], 'page');
662+
assert.equal(match.matchedPath, '/__zuul/subcat/');
663+
} else {
664+
assert.equal(match.matchedPath, '/page');
665+
}
666+
assert(!navigation.match);
667+
},
668+
navigationHandler: function (path, navigation, match) {
669+
called.push(path);
670+
if (called.length === 3) {
671+
assert.equal(match.match._[0], 'page');
672+
assert.equal(match.matchedPath, '/__zuul/subcat/');
673+
} else {
674+
assert.equal(match.matchedPath, '/page');
675+
}
676+
assert(!navigation.match);
677+
}
678+
});
679+
router.navigate('/__zuul/subcat/page', function () {
680+
assert.equal(called.length, 4);
681+
done();
682+
});
683+
});
590684
});
591685

592686
describe('Multiple active routers', function() {

0 commit comments

Comments
 (0)