REACT ROUTER
- development
- javascript
- react.js
Why React Router?
Without a router library, 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.
Installation
Install react-router usingnpm:
npm install react-router --save
Use --save flag to save the dependency to the package.json file.
Using React Router
Let's write a React component with ES6 syntax:
import { Route } from 'react-router';
const routes = (
<Route handler={App} path="/">
<Route name="about" handler={About} />
</Route>
);
We import Route component from react-router library.
Custom Path
We can specify a custom path like this:
import { Route } from 'react-router';
const routes = (
<Route handler={App} path="/">
<Route name="about" path="my-about" handler={About} />
</Route>
);
About page is now available at /my-about.
Default Route
DefaultRoute can be used to point to home page.
import { Route, DefaultRoute } from 'react-router';
const routes = (
<Route handler={App} path="/">
<DefaultRoute handler={Home} />
<Route name="about" path="my-about" handler={About} />
</Route>
);
Home page is now available at /.
NotFound Route
NotFoundRoute can be used to point to home page.
import { Route } from 'react-router';
const routes = (
<Route handler={App} path="/">
<Route name="about" handler={About} />
<NotFoundRoute handler={NotFound} />
</Route>
);
Home page is now available at /.
Nesting
You can nest routes inside another.
import { Route } from 'react-router';
const routes = (
<Route handler={App} path="/">
<Route name="users" handler={Users}>
<Route name="recent-users" path="recent" handler={RecentUsers} />
</Route>
</Route>
);
Recent users available at /users/recent.
Request Parameters
Request Parameters are added to the router with a colon, for example /user/:userId.
import { Route } from 'react-router';
const routes = (
<Route handler={App} path="/">
<Route name="users" handler={Users}>
<Route name="user" path="/user/:userId" handler={User} />
</Route>
</Route>
);
User 1234 is now available at ** /user/1234**.
Multiple request parameters can added like follows:
<Route
name="user-message"
path="/user/:userId/message/:messageId"
handler={User}
/>
This will resolve into /users/123/message/456.
Redirect Routes
Redirect can be used for redirect routes.
import { Route, Redirect } from 'react-router';
const routes = (
<Route handler={App} path="/">
<Route name="about" handler={About} />
<Redirect from="company" to="about" />
</Route>
);
Now /company will be redirected to /about.
To redirect routes with parameters:
import { Route, Redirect } from 'react-router';
const routes = (
<Route handler={App} path="/">
<Redirect from="/user/me" to="user" params={{ userId: MY_ID }} />
<Route name="user" path="/user/:userId" handler={User} />
</Route>
);
Notice the order of the routes above, it is swapped.
Including External Routes
We can import external routes like following:
import { Route, Redirect } from 'react-router';
import aboutRoutes from 'about/routes';
const routes = (
<Route handler={App} path="/">
<Redirect from="/user/me" to="user" params={{ userId: MY_ID }} />
<Route name="user" path="/user/:userId" handler={User} />
{aboutRoutes}
</Route>
);
Running The Router
We can run the router in three different ways:
- Using Hashes
- Using HTML5 History
- Using Universal Rendering
Let's look at each of these options.
Using hashes
We can pass in the routes and a callback to the run method of the Router.
import React from 'react';
import Router from 'react-router';
Router.run(routes, (Handler) => {
React.render(<Handler />, document.body);
});
The location will now be for instance http://localhost/#/about.
Using HTML5 History
Here we can pass in the routes, Router.HistoryLocation and a callback to the run method of the Router.
import React from 'react';
import Router from 'react-router';
Router.run(routes, (Handler) => {
React.render(<Handler />, document.body);
});
The location will now be for instance http://localhost/about. Notice there is no # in the URL anymore.
Using Universal Rendering
With universal rendering, we render the HTML to the client from the server.
import React from 'react';
import Router from 'react-router';
app.serve((req, res) => {
Router.run(routes, (Handler) => {
React.render(<Handler />, document.body);
});
});
Rendering the Routes
For rendering the routes, we can use RouterHandler object of the React Router library.
import React from 'react';
import { RouterHandler } from 'react-router';
class App extends React.Component {
render() {
return (
<div>
<h1>Hello World!</h1>
<RouterHandler />
</div>
);
}
}
Now the App component will render the matched router.
Accessing Router Methods
Let's quickly have a look at how we can access the router methods in ES5, ES6 and ES7.
ES5
With ES5 syntax:
var React = require('react');
var User = React.createClass({
contextTypes: {
router: React.PropTypes.func
},
render: function () {
return <h1>Hello World</h1>;
}
};
Router methods will be available in render self.context.router.
ES6
With ES6 syntax:
import React from 'react';
class User extends React.Component {
render() {
return <h1>Hello World</h1>;
}
}
User.contextTypes = {
router: React.PropTypes.func,
};
Router methods will be also available in render self.context.router.
ES7
With ES7 syntax:
import React from 'react';
class User extends React.Component {
static contextTypes = {
router: React.PropTypes.func,
};
render() {
return <h1>Hello World</h1>;
}
}
Router methods will be also available in render self.context.router.
Available Router Methods
Retrieving Parameters
We can access the parameters with the getCurrentParams method.
import React from 'react';
class User extends React.Component {
static contextTypes = {
router: React.PropTypes.func,
};
render() {
const user = this.context.router.getCurrentParams().userId;
return <h1>Hello user {user}</h1>;
}
}
The parameter will be retrieved from route /user/:userId.
Retrieving Query Parameters
We can access the query parameters with the getCurrentQuery method.
import React from 'react';
class User extends React.Component {
static contextTypes = {
router: React.PropTypes.func,
};
render() {
const message = this.context.router.getCurrentQuery().message;
return <h1>{message}</h1>;
}
}
The query parameter will be retrieved from route /user/1234?message=Hello%20World.
Retrieving Current Routes
We can access the current routes with the getCurrentRoutes method.
import React from 'react';
class User extends React.Component {
static contextTypes = {
router: React.PropTypes.func
}
render() {
const routes = this.context.router.getCurrentRoutes();
...
}
}
This will return an array of routes in nesting order.
Fetching Data
To fetch data with fetch API, let's write our own function helper.
function fetchData(state) {
return Promise.all(state.routes.filter((route) => {
return route.handler.fetchData;
}).map((route) => {
return route.handler.fetchData(state.params, state.query);
});
}
Now we can call this function inside Router.run().
import React from 'react';
import Router from 'react-router';
Router.run(routes, (Handler, state) => {
fetchData(state).then(() => {
React.render(<Handler />, document.body);
});
});
We can improve on this by adding fetch functionality to our own model.
import React from 'react';
class User extends React.Component {
static fetchData(params) {
return new Promise((resolve) => {
MyApi.loadSomething(() => {
resolve();
});
});
}
...
}
Navigating Between Routes
When using React Router, avoid using href for navigation.
import React from 'react';
class User extends React.Component {
render() {
// Don't do this!
return <a href="/about">About</a>;
}
}
Using Link Component
React Router provides a component called Link which you can use instead.
import React from 'react';
import { Link } from 'react-router';
class User extends React.Component {
render() {
return <Link to="about">About</Link>;
}
}
This will generate a link - <a href="/about">About</a>.
Adding Parameters
To add parameters, you can provide params attribute to the Link component. And for query parameters, you can add query attribute.
import React from 'react';
import { Link } from 'react-router';
class About extends React.Component {
render() {
return (
<Link to="user" params={{userId: 1234}}>User 1234</Link>
<Link to="contact" query={{message: 'Hi'}}>Say hi</Link>
);
}
}
Using makeHref
The makeHref function will generate a link for you.
import React from 'react';
class About extends React.Component {
static contextTypes = {
router: React.PropTypes.func,
};
render() {
const link = this.context.router.makeHref(
'user',
{ userId: 1234 },
{ message: 'Hi' },
);
return <a href={link}>About</a>;
}
}
The generated link will have a parameter as well as query params.
Using transitionTo
To fire a transition to anotehr URL, you can use transitioTo before React Router version 1.
import React from 'react';
class About extends React.Component {
static contextTypes = {
router: React.PropTypes.func
}
onSubmit() {
this.context.router.transitionTo('user',
{userId: 1234},
{message: 'Hi'});
}
...
}
This will transition to /user/1234?message=Hi
Using replaceWith
Without creating a new history entry, replaceWith navigates user to the given location.
import React from 'react';
class User extends React.Component {
static contextTypes = {
router: React.PropTypes.func
}
onSubmit() {
this.context.router.replaceWith('about');
}
...
}
Using goBack
To go back one step in history, we can use goBack.
import React from 'react';
class User extends React.Component {
static contextTypes = {
router: React.PropTypes.func
}
onSubmit() {
this.context.router.goBack();
}
...
}
Using goForward
To go forward one step in history, we can use goForward.
import React from 'react';
class User extends React.Component {
static contextTypes = {
router: React.PropTypes.func
}
onSubmit() {
this.context.router.goForward();
}
...
}
Using go
To go backward n steps in history.
import React from 'react';
class User extends React.Component {
static contextTypes = {
router: React.PropTypes.func
}
onSubmit() {
this.context.router.go(-2);
}
...
}
This go 2 steps back. If you want to go forward, you will have to use positive arguments. eg:- this.context.router.go(2)
Lifecycle Methods
Using willTransitionTo and willTransitionFrom
To redirect to a given location.
import React from 'react';
class User extends React.Component {
static willTransitionTo(transition) {
if (!auth.loggedIn()) {
transition.redirect('/login',
{},
{'redirect' : transition.path});
}
}
...
}
Changes in Version 1.0
Componet not handler
In version 1.0 handler has been replaced with component.
import { Route } from 'react-router';
const routes = (
<Route handler={App} path="/">
<Route name="about" handler={About} />
</Route>
);
// version 1.0
const routes = (
<Route handler={App} path="/">
<Route name="about" component={About} />
</Route>
);
Props not RouteHandler
Also RouteHandler was changed to props
import React from 'react';
class App extends React.Component {
render() {
return (
<RouterHandler />
);
}
}
// version 1.0
class App extends React.Component {
render() {
return (
{this.props.children}
);
}
}
No more named routes
Named routes were also taken out
<Route handler={App} path="/">
<Route name="user" path="/user/:userId" handler={User} />
</Route>
// version 1.0
<Route component={App} path="/">
<Route path="/user/:userId" component={User} />
</Route>
Catch all route
The Not found route becomes catch all.
<NotFoundRoute handler={NotFound} />
// version 1.0
<Route path="*" component={NotFound} />
Redirect routes
Redirect routes now have params in the URL.
<Route handler={App} path="/">
<Redirect from="/user/me" to="user" params={userId: '1234'} />
<Route name="user" path="/user/:userId" handler={User} />
</Route>
// version 1.0
<Route component={App} path="/">
<Redirect from="/user/me" to="/user/1234" />
<Route path="/user/:userId" component={User} />
</Route>
DefaultRoute not IndexRoute
IndexRoute has been replaced with DefaultRoute
<Route handler={App} path="/">
<DefaultRoute handler={Home} />
<Route name="about" handler={About} />
</Route>
// version 1.0
<Route component={App} path="/">
<IndexRoute component={Home} />
<Route path="about" component={About} />
</Route>
Mutliple components
Instead of passing an object of multiple components, in 1.0, we use multiple components.
import { Route } from 'react-router';
const routes = (
<Route component={App}>
<Route path="users" components={{ main: Users, sidebar: UsersSidebar }} />
</Route>
);
// version 1.0
import React from 'react';
class App extends React.Component {
render() {
return (
<div>
<div className="Main">{this.props.main}</div>
<div className="Sidebar">{this.props.sidebar}</div>
</div>
);
}
}
Running the router
The we run the router has changed as well.
Router.run(routes, (Handler) => {
React.render(<Handler />, document.body);
});
// version 1.0
import { history } from 'react-router/lib/HashHistory';
React.render(<Router history={history}>{routes}</Router>, document.body);
History options
The history options have chnages as well.
import { history } from 'react-router/lib/HashHistory';
import { history } from 'react-router/lib/BrowserHistory';
import { history } from 'react-router/lib/MemoryHistory';
createHref not makeHref
Instead of using makeHref, now we have params in the link.
const link = this.context.router.makeHref(
'user',
{ userId: 1234 },
{ message: 'Hi' },
);
// version 1.0
const link = this.context.router.createHref('/user/1234', { message: 'Hi' });
Link component
Link components also incorporate params in the link.
<Link to="user" params={{userId: MY_ID}}>John Doe</Link>
// version 1.0
<Link to={'/users/'} + MY_ID}>John Doe</Link>
transitionTo
The parameters have changed for transitionTo in version 1.0.
// transitionTo(pathname, params, query)
this.context.router.transitionTo('user', { userId: 1234 }, { message: 'Hi' });
// version 1.0
// transitionTo(pathname, query, state)
this.context.router.transitionTo('/user/1234', { message: 'Hi' });
willTransitionTo and willTransitionForm
Instead of above functions, version 1.0 uses onEnter and onLeave respectively.
function requireAuth(nextState, transition) {
...
}
function handler(nextState, transition) {
...
}
const routes = (
<Route component={App} path="/">
<Route path="about" component={About} onEnter={requireAuth}
onLeave={handler}/>
</Route>
);
routerWillLeave
Instead of willTransitionFrom, you can use routerWillLeave.
class User extends React.Component {
static willTransitionFrom(transition) {
...
}
...
}
// version 1.0
class User extends React.Component {
static routerWillLeave(nextState, router) {
...
}
...
}
State
Lastly, let's look at what has changed with the state in version 1.
| 0.x | 1.x |
|---|---|
| getPath() | location.pathname + location.query |
| getPathname() | location.pathname |
| getParams() | params |
| getQuery() | location.query |
| getRoutes() | routes |
| isActive(to, params, query) | history.isActive(pathname, query) |
Conclusion
This article covered most of the routing scenarios you may come across and hopefully, you will find routing with React a breeze.