Last active
October 25, 2017 02:37
-
-
Save henocdz/675efd5df38799350da27ea4219a6b73 to your computer and use it in GitHub Desktop.
React Router v4 "Static Routes"
This file contains hidden or 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 ReactDOM from 'react-dom' | |
| import { createBrowserHistory } from 'history' | |
| import { ConnectedRouter } from 'react-router-redux' | |
| // Global Styles | |
| import 'styles/app.less' | |
| // Routes config | |
| import Routes from 'routes' | |
| export const history = createBrowserHistory() | |
| const App = () => { | |
| return ( | |
| <ConnectedRouter history={ history } > | |
| <Routes /> | |
| </ConnectedRouter> | |
| ) | |
| } | |
| ReactDOM.render(<App />, document.getElementById('App')) | |
| export default App |
This file contains hidden or 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 { Route as ReactRoute, Switch } from 'react-router-dom' | |
| import Home from 'scenes/Home' | |
| import Collections from 'scenes/Collections' | |
| import AddressBook from 'scenes/AddressBook' | |
| import ImportContacts from 'scenes/ImportContacts' | |
| import Authentication from 'containers/Authentication' | |
| import App from 'containers/App' | |
| import _map from 'lodash/map' | |
| import { LoginRequiredRoute } from 'utils/router' | |
| import accounts from './accounts' | |
| import projects from './projects' | |
| /* | |
| * Routes Object. Accepts nested routes. | |
| by default all routes will be `exact: true` | |
| Example: | |
| const routes = { | |
| accounts: { | |
| path: '/accounts', | |
| container: Authentication, | |
| routes: { | |
| login: { | |
| path: '/login', | |
| requiresAuth: false, | |
| component: Login | |
| }, | |
| ... | |
| } | |
| } | |
| } | |
| * ** Route Object: ** | |
| * | |
| * path {string}: URI to be matched, nested routes will be concatenated with parent path | |
| * i.e login route => /accounts/login | |
| * container {React Component}: wrap children routes with this container | |
| * component {React Component}: component to be rendered when path is matched | |
| * if container and component are specified on the same path, component will be wrapped into Container | |
| * if no container is specified for some path, children routes won't be nested into the component | |
| * routes {Route Object}: nested routed | |
| * exact {bool}: if match must be exact to render path component (react-route-dom config param) | |
| * default: true | |
| * loginRequired {bool}: this path should be rendered only if user is logged in? | |
| * default: true | |
| * ...params {any}: react-router-dom extra params could be specified into the same object with key-value pairs | |
| * e.g `exact: false` | |
| */ | |
| const AppRoutes = { | |
| home: { | |
| path: '/', | |
| loginRequired: false, | |
| container: Authentication, | |
| component: Home | |
| }, | |
| ... | |
| notFound: { | |
| path: '*' | |
| } | |
| } | |
| /* TODO: Memoization | |
| * Returns computed given a route.name | |
| * @param {string} name: name of the route | |
| * e.g: 'root:login' | |
| * @param {object} params: all params that will be replaced if route.name was found | |
| * e.g: | |
| * route.name: 'accounts:profile' => '/accounts/profile/:user_id' | |
| * params: { user_id: 4 } | |
| * returns: '/accounts/profile/4/' | |
| */ | |
| export const reverse = (name, params = {}) => { | |
| const chunks = name.split(':') | |
| let chunkIdx = 0, | |
| chunk = chunks[chunkIdx], | |
| route = AppRoutes[chunk], | |
| uri = route.path | |
| // if is nested route (parent:child:grandchild) | |
| // compute nested path => 'parent/child/grandchild/' | |
| for (++chunkIdx; chunkIdx < chunks.length; chunkIdx++) { | |
| chunk = chunks[chunkIdx] | |
| route = route.routes[chunk] | |
| if (typeof route === 'undefined') { | |
| // no route found for given name | |
| // i.e parent.routes.child is undefined | |
| throw new Error(`Invalid sub-route: ${chunk}`) | |
| } | |
| // concatenated nested routes => route.routes | |
| uri = `${uri}${route.path}` | |
| } | |
| // replace params with passed params values | |
| // => /parent/child/:child_id => /parent/child/3 | |
| return uri.replace(/:([a-zA-Z_]+)/g, (match, key) => params[key] || key) | |
| } | |
| const DefaultContainer = props => <div> {props.children} </div> | |
| /* | |
| * Render function to be passed down to react-router's <Route render={} /> | |
| */ | |
| const _routeRenderer = (Container = DefaultContainer, component = null) => { | |
| return props => ( | |
| <Container> | |
| {!!component && React.createElement(component, props)} | |
| </Container> | |
| ) | |
| } | |
| /* | |
| * Create react-router's <Route />-s elements | |
| * if given route has `{ container: ReactComponent }` defined, | |
| * `{ container: ReactComponent }` will be wrapped into `container` | |
| * | |
| * This function is designed to be called by lodash's `map` function | |
| * | |
| * @param {Route Object} route: group of routes to be created | |
| * @param {string} name: key of the Route Object in Routes being mapped (e.g accounts: {}) | |
| * @param {object} collection: third param provided by lodash map function | |
| * @param {string} prevPath: parent path, used for nested routes => `/accounts/profile/:user_id/` | |
| * @param {React Component} parentContainer: all nested routes will inherit from parent's Container | |
| * unless a nested route has its own Container specified. | |
| * | |
| * By default all routes will be created with `LoginRequiredRoute` element | |
| * and `exact=true` | |
| */ | |
| const createRoutes = ( | |
| route, | |
| name, | |
| collection, | |
| prevPath = '', | |
| parentContainer = DefaultContainer | |
| ) => { | |
| const { | |
| routes = {}, | |
| exact = true, | |
| loginRequired = true, | |
| component: Component, | |
| container: Container = parentContainer, | |
| ...props | |
| } = route | |
| const path = `${prevPath}${route.path}` | |
| const Route = loginRequired ? LoginRequiredRoute : ReactRoute | |
| const _routes = [ | |
| <Route | |
| {...props} | |
| path={path} | |
| key={path} | |
| render={_routeRenderer(Container, Component)} | |
| exact={exact} | |
| /> | |
| ] | |
| // function to send current path (path:string) and Container (Container:ReactComponent) | |
| // to recursive function createRoutes in order to render sub-routes components | |
| const createSubRoutes = (route, name, collection) => | |
| createRoutes(route, name, collection, path, Container) | |
| // if current route-group has sub-routes create them and add them to routes-bulk | |
| // return Route otherwise. | |
| return Object.keys(routes).length | |
| ? _routes.concat(_map(routes, createSubRoutes)) | |
| : _routes | |
| } | |
| /* | |
| * <Routes /> component to be included under react-router-redux's ConnectedRouter | |
| */ | |
| export default function Routes(props) { | |
| return <Switch>{_map(AppRoutes, createRoutes)}</Switch> | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment