Note that as explained in the Readme file this is a fork of React Router v3 tailored to work within the One App ecosystem. The structure and much of the content of these docs is unchanged from the original React Router v3 documentation, however, the examples have been updated to reflect any usage or API changes we have made. Further deviation from the original React Router v3 API is expected.
One App Router is a powerful routing library built on top of React that helps you add new screens and flows to your application incredibly quickly, all while keeping the URL in sync with what's being displayed on the page.
To illustrate the problems One App Router is going to solve for you, let's build a small application without it. We will be using ES6/ES2015 syntax and language features throughout the documentation for any example code.
import React from 'react'
import { render } from 'react-dom'
const About = React.createClass({/*...*/})
const Inbox = React.createClass({/*...*/})
const Home = React.createClass({/*...*/})
const App = React.createClass({
getInitialState() {
return {
route: window.location.hash.substr(1)
}
},
componentDidMount() {
window.addEventListener('hashchange', () => {
this.setState({
route: window.location.hash.substr(1)
})
})
},
render() {
let Child
switch (this.state.route) {
case '/about': Child = About; break;
case '/inbox': Child = Inbox; break;
default: Child = Home;
}
return (
<div>
<h1>App</h1>
<ul>
<li><a href="#/about">About</a></li>
<li><a href="#/inbox">Inbox</a></li>
</ul>
<Child/>
</div>
)
}
})
render(<App />, document.body)
As the hash portion of the URL changes, <App>
will render a different <Child>
by branching on this.state.route
. Pretty straightforward stuff. But it gets complicated fast.
Imagine now that Inbox
has some nested UI at different URLs, maybe something like this overview-detail setup:
path: /inbox/messages/1234
+---------+------------+------------------------+
| About | Inbox | |
+---------+ +------------------------+
| Compose Reply Reply All Archive |
+-----------------------------------------------+
|Movie tomorrow| |
+--------------+ Subject: TPS Report |
|TPS Report From: boss@big.co |
+--------------+ |
|New Pull Reque| So ... |
+--------------+ |
|... | |
+--------------+--------------------------------+
And maybe a stats page when not viewing a message:
path: /inbox
+---------+------------+------------------------+
| About | Inbox | |
+---------+ +------------------------+
| Compose Reply Reply All Archive |
+-----------------------------------------------+
|Movie tomorrow| |
+--------------+ 10 Unread Messages |
|TPS Report | 22 drafts |
+--------------+ |
|New Pull Reque| |
+--------------+ |
|... | |
+--------------+--------------------------------+
We'd have to make our URL parsing a lot smarter, and we would end up with a lot of code to figure out which branch of nested components to be rendered at any given URL: App -> About
, App -> Inbox -> Messages -> Message
, App -> Inbox -> Messages -> Stats
, etc.
Let's refactor our app to use One App Router.
import React from 'react'
import { render } from 'react-dom'
// First we import some modules...
import { Router, Route, IndexRoute, Link, hashHistory } from '@americanexpress/one-app-router'
// Then we delete a bunch of code from App and
// add some <Link> elements...
const App = React.createClass({
render() {
return (
<div>
<h1>App</h1>
{/* change the <a>s to <Link>s */}
<ul>
<li><Link to="/about">About</Link></li>
<li><Link to="/inbox">Inbox</Link></li>
</ul>
{/*
next we replace `<Child>` with `this.props.children`
the router will figure out the children for us
*/}
{this.props.children}
</div>
)
}
})
// Finally, we render a <Router> with some <Route>s.
// It does all the fancy routing stuff for us.
render((
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="about" component={About} />
<Route path="inbox" component={Inbox} />
</Route>
</Router>
), document.body)
One App Router knows how to build nested UI for us, so we don't have to manually figure out which <Child>
component to render. For example, for a full path /about
it would build <App><About /></App>
.
Internally, the router converts your <Route>
element hierarchy to a route config. But if you're not digging the JSX you can use plain objects instead:
const routes = {
path: '/',
component: App,
indexRoute: { component: Home },
childRoutes: [
{ path: 'about', component: About },
{ path: 'inbox', component: Inbox },
]
}
render(<Router history={history} routes={routes} />, document.body)
Alright, now we're ready to nest the inbox messages inside the inbox UI.
// Make a new component to render inside of Inbox
const Message = React.createClass({
render() {
return <h3>Message</h3>
}
})
const Inbox = React.createClass({
render() {
return (
<div>
<h2>Inbox</h2>
{/* Render the child route component */}
{this.props.children}
</div>
)
}
})
render((
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
{/* add some nested routes where we want the UI to nest */}
{/* render the stats page when at `/inbox` */}
<IndexRoute component={InboxStats}/>
{/* render the message component at /inbox/messages/123 */}
<Route path="messages/:id" component={Message} />
</Route>
</Route>
</Router>
), document.body)
Now visits to URLs like inbox/messages/Jkei3c32
will match the new route and build this for you:
<App>
<Inbox>
<Message params={{ id: 'Jkei3c32' }}/>
</Inbox>
</App>
And visits to /inbox
will build this:
<App>
<Inbox>
<InboxStats/>
</Inbox>
</App>
We're going to need to know something about the message in order to fetch it from the server. Route components get some useful properties injected into them when you render, particularly the parameters from the dynamic segment of your path. In our case, :id
.
const Message = React.createClass({
componentDidMount() {
// from the path `/inbox/messages/:id`
const id = this.props.params.id
fetchMessage(id, function (err, message) {
this.setState({ message: message })
})
},
// ...
})
You can also access parameters from the query string. For instance, if you're on /foo?bar=baz
, you can access this.props.location.query.bar
to get the value "baz"
from your Route component.
That's the gist of One App Router. Application UIs are boxes inside of boxes inside of boxes; now you can keep those boxes in sync with the URL and link to them easily.
The docs about route configuration describe more of the router's features in depth.