React Router is the most popular routing library for React, which synchronizes your UI with the URL. It is built on top of React and helps you to build new screens quickly.
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
This will only cover ReactRouter versions before 1.0
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});
}
}
...
}
This will only cover ReactRouter versions before 1.0
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.