diff --git a/modules/Navigation.js b/modules/Navigation.js index 0e6027a397..c503c939e1 100644 --- a/modules/Navigation.js +++ b/modules/Navigation.js @@ -44,16 +44,16 @@ var Navigation = { * Transitions to the URL specified in the arguments by pushing * a new URL onto the history stack. */ - transitionTo(to, params, query) { - this.context.router.transitionTo(to, params, query); + transitionTo(to, params, query, data) { + this.context.router.transitionTo(to, params, query, data); }, /** * Transitions to the URL specified in the arguments by replacing * the current URL in the history stack. */ - replaceWith(to, params, query) { - this.context.router.replaceWith(to, params, query); + replaceWith(to, params, query, data) { + this.context.router.replaceWith(to, params, query, data); }, /** diff --git a/modules/Redirect.js b/modules/Redirect.js index 0b92449b82..5522f2d7c3 100644 --- a/modules/Redirect.js +++ b/modules/Redirect.js @@ -1,10 +1,11 @@ /** * Encapsulates a redirect to the given route. */ -function Redirect(to, params, query) { +function Redirect(to, params, query, data) { this.to = to; this.params = params; this.query = query; + this.data = data; } module.exports = Redirect; diff --git a/modules/TestUtils.js b/modules/TestUtils.js index 47b6a05033..cc5a2a5c96 100644 --- a/modules/TestUtils.js +++ b/modules/TestUtils.js @@ -35,7 +35,7 @@ exports.Async = React.createClass({ statics: { delay: 10, - willTransitionTo: function (transition, params, query, callback) { + willTransitionTo: function (transition, params, query, callback, data) { setTimeout(callback, exports.Async.delay); } }, @@ -61,7 +61,7 @@ exports.RedirectToFooAsync = React.createClass({ statics: { delay: 10, - willTransitionTo: function (transition, params, query, callback) { + willTransitionTo: function (transition, params, query, callback, data) { setTimeout(function () { transition.redirect('/foo'); callback(); @@ -91,7 +91,7 @@ exports.AbortAsync = React.createClass({ statics: { delay: 10, - willTransitionTo: function (transition, params, query, callback) { + willTransitionTo: function (transition, params, query, callback, data) { setTimeout(function () { transition.abort(); callback(); diff --git a/modules/Transition.js b/modules/Transition.js index f52d33a671..f8e4e00834 100644 --- a/modules/Transition.js +++ b/modules/Transition.js @@ -21,8 +21,8 @@ Transition.prototype.abort = function (reason) { this.abortReason = reason || 'ABORT'; }; -Transition.prototype.redirect = function (to, params, query) { - this.abort(new Redirect(to, params, query)); +Transition.prototype.redirect = function (to, params, query, data) { + this.abort(new Redirect(to, params, query, data)); }; Transition.prototype.cancel = function () { @@ -51,14 +51,14 @@ Transition.from = function (transition, routes, components, callback) { }, callback)(); }; -Transition.to = function (transition, routes, params, query, callback) { +Transition.to = function (transition, routes, params, query, data, callback) { routes.reduceRight(function (callback, route) { return function (error) { if (error || transition.abortReason) { callback(error); } else if (route.onEnter) { try { - route.onEnter(transition, params, query, callback); + route.onEnter(transition, params, query, callback, data); // If there is no callback in the argument list, call it automatically. if (route.onEnter.length < 4) diff --git a/modules/__tests__/Router-test.js b/modules/__tests__/Router-test.js index 3b3003a976..a7b7671d7a 100644 --- a/modules/__tests__/Router-test.js +++ b/modules/__tests__/Router-test.js @@ -192,7 +192,7 @@ describe('Router', function () { statics: { delay: Async.delay * 2, - willTransitionTo: function (transition, params, query, callback) { + willTransitionTo: function (transition, params, query, callback, data) { setTimeout(callback, LongAsync.delay); } }, @@ -585,7 +585,7 @@ describe('Router', function () { it('ignores aborting asynchronously in willTransitionTo when aborted before router.transitionTo', function (done) { var AbortAsync2 = React.createClass({ statics: { - willTransitionTo: function (transition, params, query, callback) { + willTransitionTo: function (transition, params, query, callback, data) { transition.abort(); setTimeout(callback, Async.delay); } diff --git a/modules/createRouter.js b/modules/createRouter.js index 833f5d2984..616b915f5f 100644 --- a/modules/createRouter.js +++ b/modules/createRouter.js @@ -112,7 +112,7 @@ function queryIsActive(activeQuery, query) { * * - routes (required) The route config * - location The location to use. Defaults to HashLocation when - * the DOM is available, "/" otherwise + * the DOM is available, a StaticLocation for "/" otherwise * - scrollBehavior The scroll behavior to use. Defaults to ImitateBrowserBehavior * when the DOM is available, null otherwise * - onError A function that is used to handle errors @@ -244,14 +244,14 @@ function createRouter(options) { * Transitions to the URL specified in the arguments by pushing * a new URL onto the history stack. */ - transitionTo: function (to, params, query) { + transitionTo: function (to, params, query, data) { var path = Router.makePath(to, params, query); if (pendingTransition) { // Replace so pending location does not stay in history. - location.replace(path); + location.replace(path, data); } else { - location.push(path); + location.push(path, data); } }, @@ -259,8 +259,8 @@ function createRouter(options) { * Transitions to the URL specified in the arguments by replacing * the current URL in the history stack. */ - replaceWith: function (to, params, query) { - location.replace(Router.makePath(to, params, query)); + replaceWith: function (to, params, query, data) { + location.replace(Router.makePath(to, params, query), data); }, /** @@ -292,7 +292,7 @@ function createRouter(options) { if (abortReason instanceof Cancellation) { return; } else if (abortReason instanceof Redirect) { - location.replace(Router.makePath(abortReason.to, abortReason.params, abortReason.query)); + location.replace(Router.makePath(abortReason.to, abortReason.params, abortReason.query), abortReason.data); } else { location.pop(); } @@ -304,7 +304,7 @@ function createRouter(options) { }, handleLocationChange: function (change) { - Router.dispatch(change.path, change.type); + Router.dispatch(change.path, change.type, change.data); }, /** @@ -323,13 +323,13 @@ function createRouter(options) { * transition. To resolve asynchronously, they may use the callback argument. If no * hooks wait, the transition is fully synchronous. */ - dispatch: function (path, action) { + dispatch: function (path, action, data) { Router.cancelPendingTransition(); var prevPath = state.path; var isRefreshing = action == null; - - if (prevPath === path && !isRefreshing) + var hasData = data != null; + if (prevPath === path && !isRefreshing && !hasData) return; // Nothing to do! // Record the scroll position as early as possible to @@ -355,9 +355,10 @@ function createRouter(options) { var nextRoutes = match.routes || []; var nextParams = match.params || {}; var nextQuery = match.query || {}; + var nextData = data || {}; var fromRoutes, toRoutes; - if (prevRoutes.length) { + if (data == null && prevRoutes.length) { fromRoutes = prevRoutes.filter(function (route) { return !hasMatch(nextRoutes, route, prevParams, nextParams, prevQuery, nextQuery); }); @@ -379,14 +380,15 @@ function createRouter(options) { if (error || transition.abortReason) return dispatchHandler.call(Router, error, transition); // No need to continue. - Transition.to(transition, toRoutes, nextParams, nextQuery, function (error) { + Transition.to(transition, toRoutes, nextParams, nextQuery, nextData, function (error) { dispatchHandler.call(Router, error, transition, { path: path, action: action, pathname: match.pathname, routes: nextRoutes, params: nextParams, - query: nextQuery + query: nextQuery, + data: nextData }); }); }); @@ -433,7 +435,7 @@ function createRouter(options) { }, refresh: function () { - Router.dispatch(location.getCurrentPath(), null); + Router.dispatch(location.getCurrentPath(), null, location.data); }, stop: function () { diff --git a/modules/locations/HashLocation.js b/modules/locations/HashLocation.js index a3ac0da6aa..17294f0cf2 100644 --- a/modules/locations/HashLocation.js +++ b/modules/locations/HashLocation.js @@ -4,14 +4,16 @@ var History = require('../History'); var _listeners = []; var _isListening = false; var _actionType; +var _data = []; -function notifyChange(type) { +function notifyChange(type, data) { if (type === LocationActions.PUSH) History.length += 1; var change = { path: HashLocation.getCurrentPath(), - type: type + type: type, + data: data }; _listeners.forEach(function (listener) { @@ -38,7 +40,7 @@ function onHashChange() { // manipulation. So just guess 'pop'. var curActionType = _actionType; _actionType = null; - notifyChange(curActionType || LocationActions.POP); + notifyChange(curActionType || LocationActions.POP, _data.pop()); } } @@ -80,13 +82,15 @@ var HashLocation = { } }, - push(path) { + push(path, data) { _actionType = LocationActions.PUSH; + _data.push(data); window.location.hash = path; }, - replace(path) { + replace(path, data) { _actionType = LocationActions.REPLACE; + _data.push(data); window.location.replace( window.location.pathname + window.location.search + '#' + path ); diff --git a/modules/locations/HistoryLocation.js b/modules/locations/HistoryLocation.js index d08dfa34ed..cf53a6466b 100644 --- a/modules/locations/HistoryLocation.js +++ b/modules/locations/HistoryLocation.js @@ -4,10 +4,11 @@ var History = require('../History'); var _listeners = []; var _isListening = false; -function notifyChange(type) { +function notifyChange(type, data) { var change = { path: HistoryLocation.getCurrentPath(), - type: type + type: type, + data: data }; _listeners.forEach(function (listener) { @@ -57,15 +58,15 @@ var HistoryLocation = { } }, - push(path) { + push(path, data) { window.history.pushState({ path: path }, '', path); History.length += 1; - notifyChange(LocationActions.PUSH); + notifyChange(LocationActions.PUSH, data); }, - replace(path) { + replace(path, data) { window.history.replaceState({ path: path }, '', path); - notifyChange(LocationActions.REPLACE); + notifyChange(LocationActions.REPLACE, data); }, pop: History.back, diff --git a/modules/locations/StaticLocation.js b/modules/locations/StaticLocation.js index 49dcd6a426..20305c9ae5 100644 --- a/modules/locations/StaticLocation.js +++ b/modules/locations/StaticLocation.js @@ -11,8 +11,9 @@ function throwCannotModify() { */ class StaticLocation { - constructor(path) { - this.path = path; + constructor(path, data) { + this.path = path + this.data = data; } getCurrentPath() { diff --git a/modules/locations/TestLocation.js b/modules/locations/TestLocation.js index 76cab8ad65..91d3d316b0 100644 --- a/modules/locations/TestLocation.js +++ b/modules/locations/TestLocation.js @@ -41,13 +41,13 @@ class TestLocation { }); } - push(path) { + push(path, data) { this.history.push(path); this._updateHistoryLength(); - this._notifyChange(LocationActions.PUSH); + this._notifyChange(LocationActions.PUSH, data); } - replace(path) { + replace(path, data) { invariant( this.history.length, 'You cannot replace the current path with no history' @@ -55,7 +55,7 @@ class TestLocation { this.history[this.history.length - 1] = path; - this._notifyChange(LocationActions.REPLACE); + this._notifyChange(LocationActions.REPLACE, data); } pop() {