MobX Router idea
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage: