React Routing

The full project code for the example code listed on this webpage can be downloaded from this link.

React routing is the ability to overwrite the currently displayed React Component with a different React Components when a user enters a URL or clicks a navigation element, such as a link or submit button. React Routing allows us to build a single-page web application. React routing allows the user to display a new React Component without the webpage refreshing, which provides the user with a more seamless user-experience.

React routing only manages the loading of React Components on the client-side of an application. React routing is managed in the client\src\App.js file.

React routing has no impact on the server-side of an application.

<Router>

A React <Router> is used on the client-side to route to the various components (webpages) in a React app. All of the components that can be viewed need be wrapped inside a <Router>.

<BrowserRouter>

Instead of <Router>, we can use <BrowserRouter>, which is just a sub-class of Router that allows access to the broswer history.

<Switch>

The <Switch> component is used to tell the <BrowserRouter> to only ever load one of the routes inside the <Switch> at any given time.

<Route>

Each <Route> includes a path and the component that should be loaded for that path.

A <Route> must have a path and a component that is associated with the path.

A <Router> will render its first <Route> that matches the current URL. Include the keyword exact with the path to ensure that only exact matches of a path will be routed to a component.

import React, {Component} from 'react';
import {BrowserRouter, Switch, Route} from 'react-router-dom';

import Home from './components/Home.js';
import About from './components/About.js';
import Products from './components/Products.js';


export default class App extends Component 
{
    render() 
    {
        return (
          <BrowserRouter>
            <div>        
              <Switch>
                <Route exact path="/" component={Home}/>
                <Route exact path="/home" component={Home}/>
                <Route exact path="/about" component={About}/>
                <Route exact path="/products" component={Products}/>
                <Route exact path="/products/:id" component={Products}/>
                <Route path="*" component={() => <h3>Invalid URL. Webpage does not exist</h3>}/>
              </Switch>

              <footer>
                Footer
              </footer>
            </div>
          </BrowserRouter>);
    }
}

In the code above the first two routes both point to the Home component. The first route is used to catch the situation where the user does not provide any route.

                <Route exact path="/" component={Home}/>
                <Route exact path="/home" component={Home}/>

The last route deals with any user input that is not included in the list of routes. Rather than placing this in its own JavaScript file, it is common to include it as an inline component, as shown in this example.

If an app includes a login component, you would usually point to this component instead of the error message inline component.

 <Route path="*" component={() => <h3>Invalid URL. Webpage does not exist</h3>}/>
  

<Link>

A <Link> is React's way of implementing hyperlinks. A <Link> must have a to attribute, which states which path will open when the <Link> is clicked.

In order to use <Link>, we must import Link from 'react-router-dom'.

import React, {Component} from 'react';
import {BrowserRouter, Switch, Route, Link} from 'react-router-dom';

import Home from './components/Home.js';
import About from './components/About.js';
import Products from './components/Products.js';


export default class App extends Component 
{
    render() 
    {
        return (
          <BrowserRouter>
            <div>
              <header>
                   <Link to="/home">Home</Link>
                   <Link to="/about">About</Link>
                   <Link to="/products">Products</Link>
              </header>
        
              <Switch>
                <Route exact path="/" component={Home}/>
                <Route exact path="/home" component={Home}/>
                <Route exact path="/about" component={About}/>
                <Route exact path="/products" component={Products}/>
                <Route exact path="/products/:id" component={Products}/>
                <Route path="*" component={() => <h3>Invalid URL. Webpage does not exist</h3>}/>
              </Switch>

              <footer>
                Footer
              </footer>
            </div>
          </BrowserRouter>);
    }
}

 

<Redirect>

<Redirect> will immediately redirect to a new route. It should be called at the beginning of a component's render() method. We can control <Redirect> by setting a flag in the component's state.

In order to use <Redirect>, we must import Redirect from 'react-router-dom'.

import React, {Component} from "react";
import {Redirect} from 'react-router-dom';


export default class SomeComponent extends Component
{
    constructor(props)
    {
        super(props);
        this.state = {redirectToSomePage:false}
    }

    render() 
    {  
        return (
             <div>
                 {this.state.redirectToSomePage ? <Redirect to="/SomePage"/> : null}
 
                 // normal Component render code
             </div> 
               )
    }
}

The flag this.state.redirectToSomePage is set to false in the component's constructor. When it is false, it is ignored in the render() method.

As a result of some action in the component - such as hitting a cancel button so as to exit this component and go back to the previous component - the this.state.redirectToSomePage could be set to true. The render() method will now redirect.

Browser History

We can use the this.props.history.push() method to immediately redirect to a different route from outside the render() method. This method pushes the redirected route onto the browser history, which causes it to load.

We can also use the this.props.history.replace() method. This replaces the current URL in the browser history with the new route.

React <Router> only adds the routing props to its child <Route> components. Any components within the child routes will not have the routing props. For example, a row component within a table component will not have access to the routing props.

<Redirect> versus Browser History

When coding:

As <Redirect> can only be called from inside a component's render() method, it fits into the way that React should work. At the point of rendering either the current component is rendered ot a new route is opened, which causes a new component to render.

this.props.history.push() and this.props.history.replace() do not fit into the way that React should work, because they cause the redirect to happen immediately, irrespective of the state of the component. The only arguement in favour of this approach is that it involves less code.

"Cars" Worked Example

The full project code for the example code listed on this webpage can be downloaded from this link.

To apply React routing to the "Cars" React app that we developed in the basic app lesson on the previous webpage, we only need to update the client-side client/src/App.js file.

Client-Side

 

client/src/App.js

import React, {Component} from "react"
import {BrowserRouter, Switch, Route} from "react-router-dom"

import "bootstrap/dist/css/bootstrap.css"
import "./css/App.css"

import DisplayAllCars from "./components/DisplayAllCars"

    
export default class App extends Component 
{
    render() 
    {
        return (
            <BrowserRouter>
                <Switch>                 
                    <Route exact path="/" component={DisplayAllCars}/>
                    <Route exact path="/DisplayAllCars" component={DisplayAllCars}/> 
                    <Route path="*" component={DisplayAllCars}/>                            
                </Switch>
            </BrowserRouter>
        )
    }
}

Include the Router Components

import {BrowserRouter, Switch, Route} from "react-router-dom"

App.js uses the <BrowserRouter> tag as a container that holds all of the app's other Components. The <Switch> tag will only allow one of its <Route> tag components to be displayed at a given time.

            <BrowserRouter>
                <Switch>                 
                    <Route exact path="/" component={DisplayAllCars}/>
                    <Route exact path="/DisplayAllCars" component={DisplayAllCars}/> 
                    <Route path="*" component={DisplayAllCars}/>                            
                </Switch>
            </BrowserRouter>

We should always have one default <Route> tag, followed by one or more normal <Route> tags, followed by an error-catching <Route> tag

            <BrowserRouter>
                <Switch>                 
                    <Route exact path="/" component={DisplayAllCars}/>
                    <Route exact path="/DisplayAllCars" component={DisplayAllCars}/> 
                    <Route path="*" component={DisplayAllCars}/>                            
                </Switch>
            </BrowserRouter>

There should always be one default <Route>. This will be called if the browser URL only contains the address of the app's webpage and does not include a route.

            <BrowserRouter>
                <Switch>                 
                    <Route exact path="/" component={DisplayAllCars}/>

                    ...
                           
                </Switch>
            </BrowserRouter>

Each normal <Route> should have an exact path and a component that it maps to. This component will only load if the browser URL matches the exact path.

            <BrowserRouter>
                <Switch>                 

                    ...

                    <Route exact path="/DisplayAllCars" component={DisplayAllCars}/> 

                    ...
     
                </Switch>
            </BrowserRouter>

The error-catching <Route>, which has a path="*", will catch all other browser URLs. The order in which the <route> tags are listed is important, as the first <Route> tag that is matched will be used. The catch-all path="*" will always match every route. Therefore, it must be placed last, after all other <Route> tags.

            <BrowserRouter>
                <Switch>

                    ...
                 
                    <Route path="*" component={DisplayAllCars}/>
                </Switch>
            </BrowserRouter>

Server-Side

The server-side code does not change, as the Router Component only applies to the client-side.

 
<div align="center"><a href="../versionC/index.html" title="DKIT Lecture notes homepage for Derek O&#39; Reilly, Dundalk Institute of Technology (DKIT), Dundalk, County Louth, Ireland. Copyright Derek O&#39; Reilly, DKIT." target="_parent" style='font-size:0;color:white;background-color:white'>&nbsp;</a></div>