Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions docs/api/components/RouteHandler.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ Static Lifecycle Methods
You can define static methods on your route handlers that will be called
during route transitions.

### `willTransitionTo(transition, params, query)`
### `willTransitionTo(transition, params, query, callback)`

Called when a handler is about to render, giving you the opportunity to
abort or redirect the transition. You can pause the transition while you
do some asynchonous work with `transition.wait(promise)`.
do some asynchonous work and call `callback(error)` when you're done, or
omit the callback in your argument list and it will be called for you.

See also: [transition](/docs/api/misc/transition.md)

### `willTransitionFrom(transition, component)`
### `willTransitionFrom(transition, component, callback)`

Called when an active route is being transitioned out giving you an
opportunity to abort the transition. The `component` is the current
Expand All @@ -34,13 +35,11 @@ See also: [transition](/docs/api/misc/transition.md)
var Settings = React.createClass({
statics: {
willTransitionTo: function (transition, params) {
return auth.isLoggedIn().then(function (loggedIn) {
if (!loggedIn)
return;
if (!auth.isLoggedIn()) {
transition.abort();
auth.logIn({transition: transition});
// in auth module call `transition.retry()` after being logged in
});
}
},

willTransitionFrom: function (transition, component) {
Expand Down
12 changes: 6 additions & 6 deletions modules/__tests__/Router-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ describe('Router', function () {
<Route path="/async" handler={Async}/>
];

describe('transition.wait', function () {
it('waits asynchronously in willTransitionTo', function (done) {
describe('asynchronous willTransitionTo', function () {
it('waits', function (done) {
TestLocation.history = [ '/bar' ];

var div = document.createElement('div');
Expand Down Expand Up @@ -70,7 +70,7 @@ describe('Router', function () {
});
});

it('stops waiting asynchronously in willTransitionTo on location.pop', function (done) {
it('stops waiting on location.pop', function (done) {
TestLocation.history = [ '/bar' ];

var div = document.createElement('div');
Expand Down Expand Up @@ -106,7 +106,7 @@ describe('Router', function () {
});
});

it('stops waiting asynchronously in willTransitionTo on router.transitionTo', function (done) {
it('stops waiting on router.transitionTo', function (done) {
TestLocation.history = [ '/bar' ];

var div = document.createElement('div');
Expand Down Expand Up @@ -149,7 +149,7 @@ describe('Router', function () {
});
});

it('stops waiting asynchronously in willTransitionTo on router.replaceWith', function (done) {
it('stops waiting on router.replaceWith', function (done) {
TestLocation.history = [ '/bar' ];

var div = document.createElement('div');
Expand Down Expand Up @@ -597,7 +597,7 @@ describe('Router', function () {
var Bar = React.createClass({
statics: {
willTransitionFrom: function (transition, component) {
expect(div.querySelector('#bar')).toEqual(component.getDOMNode());
expect(div.querySelector('#bar')).toBe(component.getDOMNode());
done();
}
},
Expand Down
19 changes: 10 additions & 9 deletions modules/__tests__/TestHandlers.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
var React = require('react');
var RouteHandler = require('../components/RouteHandler');
var State = require('../mixins/State');
var delay = require('when/delay');

exports.Nested = React.createClass({
render: function () {
Expand Down Expand Up @@ -36,8 +35,8 @@ exports.Async = React.createClass({
statics: {
delay: 10,

willTransitionTo: function (transition) {
transition.wait(delay(this.delay));
willTransitionTo: function (transition, params, query, callback) {
setTimeout(callback, this.delay);
}
},

Expand All @@ -62,10 +61,11 @@ exports.RedirectToFooAsync = React.createClass({
statics: {
delay: 10,

willTransitionTo: function (transition) {
transition.wait(delay(this.delay).then(function () {
willTransitionTo: function (transition, params, query, callback) {
setTimeout(function () {
transition.redirect('/foo');
}));
callback();
}, this.delay);
}
},

Expand All @@ -91,10 +91,11 @@ exports.AbortAsync = React.createClass({
statics: {
delay: 10,

willTransitionTo: function (transition) {
transition.wait(delay(this.delay).then(function () {
willTransitionTo: function (transition, params, query, callback) {
setTimeout(function () {
transition.abort();
}));
callback();
}, this.delay);
}
},

Expand Down
6 changes: 0 additions & 6 deletions modules/utils/Promise.js

This file was deleted.

103 changes: 38 additions & 65 deletions modules/utils/Transition.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,5 @@
var assign = require('react/lib/Object.assign');
var reversedArray = require('./reversedArray');
var Redirect = require('./Redirect');
var Promise = require('./Promise');

/**
* Runs all hook functions serially and calls callback(error) when finished.
* A hook may return a promise if it needs to execute asynchronously.
*/
function runHooks(hooks, callback) {
var promise;
try {
promise = hooks.reduce(function (promise, hook) {
// The first hook to use transition.wait makes the rest
// of the transition async from that point forward.
return promise ? promise.then(hook) : hook();
}, null);
} catch (error) {
return callback(error); // Sync error.
}

if (promise) {
// Use setTimeout to break the promise chain.
promise.then(function () {
setTimeout(callback);
}, function (error) {
setTimeout(function () {
callback(error);
});
});
} else {
callback();
}
}

/**
* Calls the willTransitionFrom hook of all handlers in the given matches
Expand All @@ -40,23 +8,27 @@ function runHooks(hooks, callback) {
* Calls callback(error) when finished.
*/
function runTransitionFromHooks(transition, routes, components, callback) {
components = reversedArray(components);

var hooks = reversedArray(routes).map(function (route, index) {
return function () {
var handler = route.handler;

if (!transition.isAborted && handler.willTransitionFrom)
return handler.willTransitionFrom(transition, components[index]);

var promise = transition._promise;
transition._promise = null;

return promise;
var runHooks = routes.reduce(function (callback, route, index) {
return function (error) {
if (error || transition.isAborted) {
callback(error);
} else if (route.handler.willTransitionFrom) {
try {
route.handler.willTransitionFrom(transition, components[index], callback);

// If there is no callback in the argument list, call it automatically.
if (route.handler.willTransitionFrom.length < 3)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know 3 is the magic number, but what is this 3 all about?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh nvm, args length, I was thinking we were collecting hooks or something ...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll make the comment clearer.

callback();
} catch (e) {
callback(e);
}
} else {
callback();
}
};
});
}, callback);

runHooks(hooks, callback);
runHooks();
}

/**
Expand All @@ -65,21 +37,27 @@ function runTransitionFromHooks(transition, routes, components, callback) {
* handler. Calls callback(error) when finished.
*/
function runTransitionToHooks(transition, routes, params, query, callback) {
var hooks = routes.map(function (route) {
return function () {
var handler = route.handler;

if (!transition.isAborted && handler.willTransitionTo)
handler.willTransitionTo(transition, params, query);

var promise = transition._promise;
transition._promise = null;

return promise;
var runHooks = routes.reduceRight(function (callback, route) {
return function (error) {
if (error || transition.isAborted) {
callback(error);
} else if (route.handler.willTransitionTo) {
try {
route.handler.willTransitionTo(transition, params, query, callback);

// If there is no callback in the argument list, call it automatically.
if (route.handler.willTransitionTo.length < 4)
callback();
} catch (e) {
callback(e);
}
} else {
callback();
}
};
});
}, callback);

runHooks(hooks, callback);
runHooks();
}

/**
Expand All @@ -93,7 +71,6 @@ function Transition(path, retry) {
this.abortReason = null;
this.isAborted = false;
this.retry = retry.bind(this);
this._promise = null;
}

assign(Transition.prototype, {
Expand All @@ -112,10 +89,6 @@ assign(Transition.prototype, {
this.abort(new Redirect(to, params, query));
},

wait: function (value) {
this._promise = Promise.resolve(value);
},

from: function (routes, components, callback) {
return runTransitionFromHooks(this, routes, components, callback);
},
Expand Down
6 changes: 4 additions & 2 deletions modules/utils/createRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ function createRouter(options) {
* all route handlers we're transitioning to.
*
* Both willTransitionFrom and willTransitionTo hooks may either abort or redirect the
* transition. To resolve asynchronously, they may use transition.wait(promise). If no
* transition. To resolve asynchronously, they may use the callback argument. If no
* hooks wait, the transition is fully synchronous.
*/
dispatch: function (path, action, callback) {
Expand Down Expand Up @@ -374,7 +374,9 @@ function createRouter(options) {
var transition = new Transition(path, this.replaceWith.bind(this, path));
pendingTransition = transition;

transition.from(fromRoutes, components, function (error) {
var fromComponents = components.slice(prevRoutes.length - fromRoutes.length);

transition.from(fromRoutes, fromComponents, function (error) {
if (error || transition.isAborted)
return callback.call(router, error, transition);

Expand Down
5 changes: 0 additions & 5 deletions modules/utils/reversedArray.js

This file was deleted.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@
"react": "0.12.x"
},
"dependencies": {
"qs": "2.3.3",
"when": "3.4.6"
"qs": "2.3.3"
},
"tags": [
"react",
Expand Down