Skip to content

Instantly share code, notes, and snippets.

@mohsen1
Created September 26, 2016 01:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mohsen1/85a594722b34ed4f62c3f7b116ea8217 to your computer and use it in GitHub Desktop.
Save mohsen1/85a594722b34ed4f62c3f7b116ea8217 to your computer and use it in GitHub Desktop.
MobX Router idea
import {merge} from 'lodash';
import {autorun} from 'mobx';
// TODO: find a good polyfill for this
declare const URLSearchParams;
const routeMatcher = require("route-matcher").routeMatcher;
const createBrowserHistory = require('history/createBrowserHistory').default;
/*
* A store have to implement these two getters to be routable
* both getters should return string
*/
export interface IRoutableStore {
currentPath: string; // example: '/foo'
currentQueryString: string; // example: '?bar=1&baz=2'
}
/*
* list of routes be declared in a map of route to corresponding functions
*
* Routes can have path parameters in them, see "route-matcher" for how path
* parameters work.
*/
export interface IRoutesMap {
[route: string]: (params: {[key: string]: string})=> any
}
/*
* A super simple router for MobX
*/
export class Router {
private _routes: IRoutesMap;
private _browserHistory = createBrowserHistory();
/*
* Given routes map and a store, creates a browser history binding that hooks
* the routes to the browser history in a two way binding system
*/
constructor(routes: IRoutesMap, store: IRoutableStore) {
this._routes = routes;
// listen for location pop events
this._browserHistory.listen((newLocation: Location, action: string)=> {
if (action === 'POP') {
return this.match(newLocation);
}
});
// start
this.match(window.location);
// listen to store path and query string changes and push to history
autorun(() => {
const path = store.currentPath;
const qs = store.currentQueryString;
if (path !== window.location.pathname || qs !== window.location.search) {
window.history.pushState(null, null, path + qs)
}
});
}
/*
* Get current query params as an object hash
*
* @TODO query strings can have multiple values for the same key (e.g foo=1&foo=2)
*
*/
get currentQueryParams(): {} {
const search = window.location.search;
// TODO polyfill this
const searchParams = new URLSearchParams(search);
const result = {};
searchParams.forEach((value, key) => {
result[key] = value;
});
return result;
}
/*
* Matches a location to one of the routes
*
* @TODO: default route?
*/
match(aLocation: Location): void {
for (const route of Object.keys(this._routes)) {
const matcher = routeMatcher(route);
const result = matcher.parse(aLocation.pathname);
if (result) {
const queryParams = this.currentQueryParams;
// TODO: if isFunction
this._routes[route](merge(result, queryParams));
}
}
}
}
@mohsen1
Copy link
Author

mohsen1 commented Sep 26, 2016

Usage:

export function startRouter(store) {
    const routes = {
        '/': ()=> store.goHome(),

        // page is a query param
        '/:id': ({id, page})=> store.showDocument(id, page)
    }
    return new Router(routes, store);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment