Created
September 14, 2015 04:56
-
-
Save jquense/109f86a6b443345bfd76 to your computer and use it in GitHub Desktop.
React router with areas and names
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 React from 'react'; | |
import { Router as BaseRouter } from 'react-router'; | |
import { getParamNames, formatPattern } from 'react-router/lib/PatternUtils'; | |
import { createRoutes } from 'react-router/lib/RouteUtils' | |
import createBrowserHistory from 'history/lib/createBrowserHistory'; | |
import useBeforeUnload from 'history/lib/useBeforeUnload'; | |
import invariant from 'app/utils/invariant'; | |
import { each, pick, omit, sortBy } from 'lodash'; | |
const EMPTY_AREA = '@@none'; | |
const routeHash = Object.create(null); | |
let history = useBeforeUnload(createBrowserHistory)(); | |
export let router; | |
export function routeUrl({ area = EMPTY_AREA, name, ...params }){ | |
invariant(routeHash[area], | |
'the provided area: ' + area + ' is not valid. ' + | |
'Valid areas are: ' + Object.keys(routeHash).filter( n => n !== EMPTY_AREA).join(', ')); | |
let routes = routeHash[area][name] || []; | |
invariant(routes.length, | |
'the provided name: ' + name + ' is not valid. ' + | |
'Valid names are: ' + Object.keys(routeHash[area]).join(', ')); | |
let candidates = routes | |
.map(route => { | |
let names = getParamNames(route); | |
return { | |
route, | |
needed: names.length, | |
params: pick(params, names), | |
query: omit(params, names) | |
} | |
}) | |
.filter(i => Object.keys(i.params).length >= i.needed) | |
invariant(candidates.length, | |
'No routes matched the provided infomation, you may be missing a parameter') | |
let match = sortBy(candidates, i => i.needed.length)[0]; | |
return formatPattern(match.route, match.params) | |
} | |
export function isActive(name){ | |
let routes = router && router.state.routes; | |
return routes && routes.some(route => route.name === name) | |
} | |
export let Router = React.createClass({ | |
componentWillMount(){ | |
this._routes = createRoutes(this.props.children); | |
buildHash(this._routes) | |
}, | |
render(){ | |
return ( | |
<BaseRouter | |
ref={r => router = r} | |
history={history} | |
{...this.props} | |
children={this._routes} | |
/> | |
) | |
} | |
}) | |
function combinePath(path, parent){ | |
if (path[0] === '/') return path | |
return parent ? (parent + '/' + path) : ('/' + path) | |
} | |
function buildHash(routes, parentPath, parentArea) { | |
forEach(routes, (name, area, path, { childRoutes, getChildRoutes, providesPaths }) => { | |
path = combinePath(path, parentPath) | |
if (name){ | |
invariant(!parentArea || (parentArea && !area), | |
`the route: ${name}: '${path}' cannot specify an area if it is inside an area already`); | |
area = parentArea || area || EMPTY_AREA; | |
routeHash[area] = routeHash[area] || {}; | |
routeHash[area][name] = (routeHash[area][name] || []).concat(path); | |
} | |
if ( getChildRoutes ) { | |
invariant(providesPaths, | |
'Lazy loading routes requires that you specify the paths in `providesPaths`') | |
buildHash(providesPaths, path, area) | |
} | |
if (childRoutes) | |
buildHash(childRoutes, path, area) | |
}) | |
} | |
function forEach(routeOrHash, cb){ | |
each(routeOrHash, (child, key) => { | |
var name, area, path, props; | |
if (!child) return; | |
else if (typeof child === 'string') | |
name = key, path = child; | |
else | |
({ name, area, path, ...props } = child) | |
cb(name, area, path, props || {}) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
and then you can use routes like: