react-router
Version:
A complete routing library for React
148 lines (125 loc) • 4.26 kB
JavaScript
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
import { loopAsync } from './AsyncUtils';
var PendingHooks = function PendingHooks() {
var _this = this;
_classCallCheck(this, PendingHooks);
this.hooks = [];
this.add = function (hook) {
return _this.hooks.push(hook);
};
this.remove = function (hook) {
return _this.hooks = _this.hooks.filter(function (h) {
return h !== hook;
});
};
this.has = function (hook) {
return _this.hooks.indexOf(hook) !== -1;
};
this.clear = function () {
return _this.hooks = [];
};
};
var enterHooks = new PendingHooks();
var changeHooks = new PendingHooks();
function createTransitionHook(hook, route, asyncArity, pendingHooks) {
var isSync = hook.length < asyncArity;
var transitionHook = function transitionHook() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
hook.apply(route, args);
if (isSync) {
var callback = args[args.length - 1];
// Assume hook executes synchronously and
// automatically call the callback.
callback();
}
};
pendingHooks.add(transitionHook);
return transitionHook;
}
function getEnterHooks(routes) {
return routes.reduce(function (hooks, route) {
if (route.onEnter) hooks.push(createTransitionHook(route.onEnter, route, 3, enterHooks));
return hooks;
}, []);
}
function getChangeHooks(routes) {
return routes.reduce(function (hooks, route) {
if (route.onChange) hooks.push(createTransitionHook(route.onChange, route, 4, changeHooks));
return hooks;
}, []);
}
function runTransitionHooks(length, iter, callback) {
if (!length) {
callback();
return;
}
var redirectInfo = void 0;
function replace(location) {
redirectInfo = location;
}
loopAsync(length, function (index, next, done) {
iter(index, replace, function (error) {
if (error || redirectInfo) {
done(error, redirectInfo); // No need to continue.
} else {
next();
}
});
}, callback);
}
/**
* Runs all onEnter hooks in the given array of routes in order
* with onEnter(nextState, replace, callback) and calls
* callback(error, redirectInfo) when finished. The first hook
* to use replace short-circuits the loop.
*
* If a hook needs to run asynchronously, it may use the callback
* function. However, doing so will cause the transition to pause,
* which could lead to a non-responsive UI if the hook is slow.
*/
export function runEnterHooks(routes, nextState, callback) {
enterHooks.clear();
var hooks = getEnterHooks(routes);
return runTransitionHooks(hooks.length, function (index, replace, next) {
var wrappedNext = function wrappedNext() {
if (enterHooks.has(hooks[index])) {
next.apply(undefined, arguments);
enterHooks.remove(hooks[index]);
}
};
hooks[index](nextState, replace, wrappedNext);
}, callback);
}
/**
* Runs all onChange hooks in the given array of routes in order
* with onChange(prevState, nextState, replace, callback) and calls
* callback(error, redirectInfo) when finished. The first hook
* to use replace short-circuits the loop.
*
* If a hook needs to run asynchronously, it may use the callback
* function. However, doing so will cause the transition to pause,
* which could lead to a non-responsive UI if the hook is slow.
*/
export function runChangeHooks(routes, state, nextState, callback) {
changeHooks.clear();
var hooks = getChangeHooks(routes);
return runTransitionHooks(hooks.length, function (index, replace, next) {
var wrappedNext = function wrappedNext() {
if (changeHooks.has(hooks[index])) {
next.apply(undefined, arguments);
changeHooks.remove(hooks[index]);
}
};
hooks[index](state, nextState, replace, wrappedNext);
}, callback);
}
/**
* Runs all onLeave hooks in the given array of routes in order.
*/
export function runLeaveHooks(routes, prevState) {
for (var i = 0, len = routes.length; i < len; ++i) {
if (routes[i].onLeave) routes[i].onLeave.call(routes[i], prevState);
}
}