React Router

What is React Router?

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 using npm:

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:

  1. Using Hashes
  2. Using HTML5 History
  3. 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 e 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

In order 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 paremeter 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 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.

Be first to comment

Leave a Reply