Skip to content

Commit 75c6206

Browse files
committed
[added] router.stop()
This commit adds a router.stop() method that may be used to prevent a router from listening to further changes to location. This method is also called automatically when a router component is removed from the DOM. Thanks to @gaearon and @rpflorence for the initial thrust to get this functionality into the router. This work doesn't entirely supercede #606, but duplicates much of the work done there.
1 parent 623af72 commit 75c6206

File tree

4 files changed

+63
-55
lines changed

4 files changed

+63
-55
lines changed

modules/locations/HashLocation.js

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -67,38 +67,33 @@ var HashLocation = {
6767
// Do this BEFORE listening for hashchange.
6868
ensureSlash();
6969

70-
if (_isListening)
71-
return;
70+
if (!_isListening) {
71+
if (window.addEventListener) {
72+
window.addEventListener('hashchange', onHashChange, false);
73+
} else {
74+
window.attachEvent('onhashchange', onHashChange);
75+
}
7276

73-
if (window.addEventListener) {
74-
window.addEventListener('hashchange', onHashChange, false);
75-
} else {
76-
window.attachEvent('onhashchange', onHashChange);
77+
_isListening = true;
7778
}
78-
79-
_isListening = true;
8079
},
8180

8281
removeChangeListener: function(listener) {
83-
for (var i = 0, l = _changeListeners.length; i < l; i ++) {
84-
if (_changeListeners[i] === listener) {
85-
_changeListeners.splice(i, 1);
86-
break;
82+
_changeListeners = _changeListeners.filter(function (l) {
83+
return l !== listener;
84+
});
85+
86+
if (_changeListeners.length === 0) {
87+
if (window.removeEventListener) {
88+
window.removeEventListener('hashchange', onHashChange, false);
89+
} else {
90+
window.removeEvent('onhashchange', onHashChange);
8791
}
88-
}
89-
90-
if (window.removeEventListener) {
91-
window.removeEventListener('hashchange', onHashChange, false);
92-
} else {
93-
window.removeEvent('onhashchange', onHashChange);
94-
}
9592

96-
if (_changeListeners.length === 0)
9793
_isListening = false;
94+
}
9895
},
9996

100-
101-
10297
push: function (path) {
10398
_actionType = LocationActions.PUSH;
10499
window.location.hash = Path.encode(path);

modules/locations/HistoryLocation.js

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,38 +38,33 @@ var HistoryLocation = {
3838
addChangeListener: function (listener) {
3939
_changeListeners.push(listener);
4040

41-
if (_isListening)
42-
return;
41+
if (!_isListening) {
42+
if (window.addEventListener) {
43+
window.addEventListener('popstate', onPopState, false);
44+
} else {
45+
window.attachEvent('popstate', onPopState);
46+
}
4347

44-
if (window.addEventListener) {
45-
window.addEventListener('popstate', onPopState, false);
46-
} else {
47-
window.attachEvent('popstate', onPopState);
48+
_isListening = true;
4849
}
49-
50-
_isListening = true;
5150
},
5251

5352
removeChangeListener: function(listener) {
54-
for (var i = 0, l = _changeListeners.length; i < l; i ++) {
55-
if (_changeListeners[i] === listener) {
56-
_changeListeners.splice(i, 1);
57-
break;
53+
_changeListeners = _changeListeners.filter(function (l) {
54+
return l !== listener;
55+
});
56+
57+
if (_changeListeners.length === 0) {
58+
if (window.addEventListener) {
59+
window.removeEventListener('popstate', onPopState);
60+
} else {
61+
window.removeEvent('popstate', onPopState);
5862
}
59-
}
60-
61-
if (window.addEventListener) {
62-
window.removeEventListener('popstate', onPopState);
63-
} else {
64-
window.removeEvent('popstate', onPopState);
65-
}
6663

67-
if (_changeListeners.length === 0)
6864
_isListening = false;
65+
}
6966
},
7067

71-
72-
7368
push: function (path) {
7469
window.history.pushState({ path: path }, '', Path.encode(path));
7570
History.length += 1;

modules/locations/TestLocation.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ var TestLocation = {
2828
updateHistoryLength();
2929
},
3030

31-
removeChangeListener: function () {},
32-
3331
push: function (path) {
3432
TestLocation.history.push(path);
3533
updateHistoryLength();

modules/utils/createRouter.js

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,14 @@ function createRouter(options) {
157157
var state = {};
158158
var nextState = {};
159159
var pendingTransition = null;
160+
var changeListener = null;
161+
162+
function cancelPendingTransition() {
163+
if (pendingTransition) {
164+
pendingTransition.abort(new Cancellation);
165+
pendingTransition = null;
166+
}
167+
}
160168

161169
function updateState() {
162170
state = nextState;
@@ -192,6 +200,7 @@ function createRouter(options) {
192200

193201
defaultRoute: null,
194202
notFoundRoute: null,
203+
isRunning: false,
195204

196205
/**
197206
* Adds routes to this router from the given children object (see ReactChildren).
@@ -317,10 +326,7 @@ function createRouter(options) {
317326
* hooks wait, the transition is fully synchronous.
318327
*/
319328
dispatch: function (path, action, callback) {
320-
if (pendingTransition) {
321-
pendingTransition.abort(new Cancellation);
322-
pendingTransition = null;
323-
}
329+
cancelPendingTransition();
324330

325331
var prevPath = state.path;
326332
if (prevPath === path)
@@ -396,6 +402,11 @@ function createRouter(options) {
396402
* Router.*Location objects (e.g. Router.HashLocation or Router.HistoryLocation).
397403
*/
398404
run: function (callback) {
405+
invariant(
406+
!this.isRunning,
407+
'Router is already running'
408+
);
409+
399410
var dispatchHandler = function (error, transition) {
400411
pendingTransition = null;
401412

@@ -412,7 +423,7 @@ function createRouter(options) {
412423
router.dispatch(location, null, dispatchHandler);
413424
} else {
414425
// Listen for changes to the location.
415-
var changeListener = function (change) {
426+
changeListener = function (change) {
416427
router.dispatch(change.path, change.type, dispatchHandler);
417428
};
418429

@@ -421,11 +432,20 @@ function createRouter(options) {
421432

422433
// Bootstrap using the current path.
423434
router.dispatch(location.getCurrentPath(), null, dispatchHandler);
435+
436+
this.isRunning = true;
424437
}
425438
},
426439

427-
teardown: function() {
428-
location.removeChangeListener(this.changeListener);
440+
stop: function () {
441+
cancelPendingTransition();
442+
443+
if (location.removeChangeListener && changeListener) {
444+
location.removeChangeListener(changeListener);
445+
changeListener = null;
446+
}
447+
448+
this.isRunning = false;
429449
}
430450

431451
},
@@ -461,8 +481,8 @@ function createRouter(options) {
461481
this.setState(state);
462482
},
463483

464-
componentWillUnmount: function() {
465-
router.teardown();
484+
componentWillUnmount: function () {
485+
router.stop();
466486
},
467487

468488
render: function () {

0 commit comments

Comments
 (0)