Copyright Derek O'Reilly, Dundalk Institute of Technology (DkIT), Dundalk, Co. Louth, Ireland.
React client-side routing is the ability to overwrite the currently displayed React component with a different React component when a user enters a URL or clicks a navigation element, such as a link or submit button. React client-side routing allows us to build a single-page web application. React client-side 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 client-side routing is similar to document.getElementById().innerHTML, in that it can be used to overwrite the contents of a webpage or part of a webpage.
React client-side routing only affects the client-side of an application. React client-side routing has no impact on the server-side of an application.
React client-side routing is managed in the client\src\App.js file.
A React <BrowserRouter> 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 <BrowserRouter>.
We use routes in the client/src/App.js file. In order to use <BrowserRouter> (or any of the other components discussed in this section of the notes), we must import it from react-router-dom, as shown in the code below:
...
import {BrowserRouter} from 'react-router-dom'
...
The <Switch> component is used to tell the <BrowserRouter> to only ever load one of the routes inside the <Switch> at any given time.
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.
Below is an example of an App.js file that uses BrowserRouter, Switch, and Route
import React from "react"
import {BrowserRouter, Switch, Route} from "react-router-dom"
import {Home} from "./components/Home"
import {About} from "./components/About"
import {DisplayProduct} from "./components/DisplayProduct"
import {DisplayAllProducts} from "./components/DisplayAllProducts"
import "bootstrap/dist/css/bootstrap.css"
import "./css/App.css"
export const App = props =>
{
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home}/>
<Route exact path="/home" component={Home}/>
<Route exact path="/about" component={About}/>
<Route exact path="/product/:id" component={DisplayProduct}/>
<Route exact path="/products" component={DisplayAllProducts}/>
<Route path="*" component={() => <h3>Invalid URL. Webpage does not exist</h3>}/>
</Switch>
<footer>
Footer
</footer>
</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>}/>
The code that points to the DisplayProduct component includes an id. This allows us to pass a variable (parameter) to the DisplayProduct component. The : is used in the path to specify that we shall be passing a variable to the component. In the code below, :id means we shall be passing a variable called id.
<Route exact path="/product/:id" component={DisplayProduct}/>
The id included in the path will automatically be included as part of the props object that is passed to the DisplayProduct component. Within the DisplayProduct component code, the id can be accessed using the code below.
props.match.params.id
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 from 'react'
import {BrowserRouter, Switch, Route, Link} from 'react-router-dom'
import Home from './components/Home.js'
import About from './components/About.js'
import DisplayAllProducts from './components/DisplayAllProducts.js'
export const App = props =>
{
return (
<BrowserRouter>
<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="/product/:id" component={DisplayProduct}/>
<Route exact path="/products" component={DisplayAllProducts}/>
<Route path="*" component={() => <h3>Invalid URL. Webpage does not exist</h3>}/>
</Switch>
<footer>
Footer
</footer>
</BrowserRouter>
)
}
<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, {useState} from "react"
import {Redirect} from 'react-router-dom'
export const SomeComponent = props =>
{
const [redirectToSomePage, setRedirectToSomePage] = useState(false)
...
return (
<div>
{redirectToSomePage ? <Redirect to="/SomePage"/> : null}
// normal Component render code
...
</div>
)
}
The flag 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 redirectToSomePage could be set to true. The render() method will now redirect.
We can use the 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 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.
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.
props.history.push() and 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.
Open the react_app_functional project from the previous section in these notes. Change the code in client/src/App.js so that it uses client-side routing.
Test that your code catches invalid URLs.
The full project code for the "Cars" Worked Example that is described below can be downloaded from this link.
To apply React routing to the "Cars" React app that we developed using functional components in the functional components section of these notes, we only need to update the client-side client/src/App.js file.
import React, {Component} from "react"
import {BrowserRouter, Switch, Route} from "react-router-dom"
import DisplayAllCars from "./components/DisplayAllCars"
import "bootstrap/dist/css/bootstrap.css"
import "./css/App.css"
export const App = props =>
{
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={DisplayAllCars}/>
<Route exact path="/cars" component={DisplayAllCars}/>
<Route path="*" component={() => <h3>Invalid URL. Webpage does not exist</h3>}/>
</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="/cars" component={DisplayAllCars}/> <Route path="*" component={() => <h3>Invalid URL. Webpage does not exist</h3>}/> </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="/cars" component={DisplayAllCars}/>
<Route path="*" component={() => <h3>Invalid URL. Webpage does not exist</h3>}/>
</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="/cars" 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={() => <h3>Invalid URL. Webpage does not exist</h3>}/>
</Switch>
</BrowserRouter>
The server-side code does not change, as the Router Component only applies to the client-side.
Copyright Derek O' Reilly, Dundalk Institute of Technology (DkIT), Dundalk, Co. Louth, Ireland.