Skip to content

Instantly share code, notes, and snippets.

@acdlite acdlite/app.js
Last active Nov 4, 2018

Embed
What would you like to do?
Minimal router using existing React Router primitives
const history = createHistory();
const router = createReactRouter(routes);
history.listen((location => {
router.match(location, (err, state, redirectInfo) => {
// Integrate with external state library (Redux), or render directly
React.render(
<RouterComponent {...state} />;
);
});
});
// On the server, just omit history
router.match(createLocation(path), (err, state, redirectInfo) => {
React.renderToString(
<RouterComponent {...state} />;
);
});
/**
* type RouterState = {
* activeRoutes: Array<Route>,
* params: Object
* }
*/
import matchRoutes from './matchRoutes';
import runTransitionHooks from './runTransitionHooks';
/**
* Base createRouter() function with no React-specific functionality,
* because #modularity
*/
export function createRouter(routes) {
let state;
function match(location, callback) {
matchRoutes(routes, location, (error, nextState) => {
if (!error) {
state = nextState;
}
callback(error, nextState);
});
}
function getState() {
return state;
}
return {
match,
getState
};
}
/**
* Router enhancer runs transition hooks when matching
*/
export function useTransitionHooks(next) {
return routes => {
const router = next(routes);
function match(location, callback) {
const prevState = router.getState();
router.match(location, (error, nextState) => {
if (error || nextState == null) {
callback(error, null);
return;
}
runTransitionHooks(prevState, nextState, (transitionError, redirectInfo) => {
if (error || redirectInfo) {
callback(error, null, redirectInfo);
}
callback(null, nextState);
});
});
}
return {
...router,
match
};
};
}
/**
* Router-creating function with transition hook functionality added
*/
export const createReactRouter = useTransitionHooks(createRouter);
@acdlite

This comment has been minimized.

Copy link
Owner Author

commented Aug 17, 2015

New router API proposal and (partial) implementation. (Missing piece is <RouterComponent> which renders for a given router state.)

This was surprisingly easy to whip together — all the hard work was already done with matchRoutes() and runTransitionHooks(). Awesome job, React Router team!

Key points:

  • Separates history listening, route matching, and rendering.
  • Easy to integrate with libraries other than React — could split into separate project, like history.
  • Minimizes state. Only state is location (handled by history) and activeRoutes.
@acdlite

This comment has been minimized.

Copy link
Owner Author

commented Aug 17, 2015

Another cool thing is that there's no separate API for server-side rendering: just omit history and pass a location directly to router.match().

@acdlite

This comment has been minimized.

Copy link
Owner Author

commented Aug 17, 2015

@mjackson

This comment has been minimized.

Copy link

commented Aug 17, 2015

I really like the approach that you're taking with the react function, making it work like a piece of middleware. I wonder if we can just pass the createRouter function directly to it, like you did here with the enableQueries function. So it would look like:

var router = useTransitionHooks(createRouter)();
@acdlite

This comment has been minimized.

Copy link
Owner Author

commented Aug 17, 2015

@mjackson That's essentially what this line is doing

export const createReactRouter = react(createRouter)

:)

@acdlite

This comment has been minimized.

Copy link
Owner Author

commented Aug 17, 2015

Oh but you're right that useTransitionHooks() is a better name for that function. Nothing React-y about it.

@acdlite

This comment has been minimized.

Copy link
Owner Author

commented Aug 17, 2015

And yeah I think with an API like this there's potential for a really nice middleware ecosystem, a la Redux.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.