Skip to content
This repository has been archived by the owner on Mar 21, 2019. It is now read-only.

Commit

Permalink
run onEnter and onLeave routes in order and not in parallel
Browse files Browse the repository at this point in the history
  • Loading branch information
michalkvasnicak committed Dec 15, 2015
1 parent 16b2a2c commit 45ee4d3
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 6 deletions.
50 changes: 44 additions & 6 deletions src/Router.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 30,25 @@ function parseQuery(search) {
return {};
}

/**
* Reduce promises
*
* @param {Function} fn
* @param {*} start
* @returns {Function}
*/
function reduce(fn, start) {
return (val) => {
const values = Array.isArray(val) ? val : [val];

return values.reduce((promise, curr) => {
return promise.then((prev) => {
return fn(prev, curr);
});
}, Promise.resolve(start));
};
}

function instantiateRoutes(routes) {
return routes.map((definition) => {
const normalized = normalizeRouteDefinition(definition);
Expand Down Expand Up @@ -239,11 258,31 @@ export default class Router {
return _resolve();
}

return Promise.all(
route[handlers].map(
(handler) => runWrappedHandler(handler, args, this.handlerWrappers[handlers])
)
).then(_resolve, _reject);
// if running onEnter, run them from parent to child
// if onLeave, run them from child to parent

// run
return reduce(
(acc, current) => {
try {
const result = runWrappedHandler(current, args, this.handlerWrappers[handlers]);

if (result && typeof result.then === 'function') {
return result.then(res => {
acc.push(res);

return acc;
});
}

acc.push(result);

return Promise.resolve(acc);
} catch (e) {
return Promise.reject(e);
}
}, []
)(handlers === 'onEnter' ? route[handlers] : route[handlers].reverse()).then(_resolve, _reject);
});
};

Expand Down Expand Up @@ -300,7 339,6 @@ export default class Router {
this._callEventListeners('changeStart', this._currentRoute, resolvedRoute, this);

// call on leave in order (so we can cancel transition)
// todo call transition hooks in order?
runRouteHandlers('onLeave', this._currentRoute, resolvedRoute, this).then(
() => runRouteHandlers('onEnter', resolvedRoute, this._currentRoute, resolvedRoute, this).then(
() => resolveComponents(resolvedRoute.components).then(
Expand Down
74 changes: 74 additions & 0 deletions test/Router.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 493,80 @@ describe('Router', () => {
}
);
});

it('resolves onEnter handlers in order', () => {
const onTransition = spy();
const onEnter1 = spy(() => new Promise(resolve => {
setTimeout(resolve, 150);
}));
const onEnter2 = spy(() => Promise.resolve());

const router = new Router(
[
{
path: 'a',
component: 'dashboard',
onEnter: onEnter1,
children: [
{
path: 'b',
component: 'newmessage',
onEnter: onEnter2
}
]
}
],
createMemoryHistory(),
onTransition
);

return router.run('/a/b').then(
(resolvedRoute) => {
expect(router.currentRoute()).not.to.be.equal(null);
expect(resolvedRoute).to.be.equal(router.currentRoute());
expect(onEnter1.calledBefore(onEnter2)).to.be.equal(true);
}
);
});

it('resolves onLeave handlers in order', () => {
const onTransition = spy();
const onLeave1 = spy(() => new Promise(resolve => {
setTimeout(resolve, 150);
}));
const onLeave2 = spy(() => Promise.resolve());

const router = new Router(
[
{
path: '/',
component: 'a'
},
{
path: 'a',
component: 'dashboard',
onLeave: onLeave1,
children: [
{
path: 'b',
component: 'newmessage',
onLeave: onLeave2
}
]
}
],
createMemoryHistory(),
onTransition
);

return router.run('/a/b').then(
() => {
return router.run('/').then(() => {
expect(onLeave2.calledBefore(onLeave1)).to.be.equal(true);
});
}
);
});
});

describe('handler wrapping', () => {
Expand Down

0 comments on commit 45ee4d3

Please sign in to comment.