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.
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 DisplayAllCars from './components/DisplayAllCars.js' import DisplayCar from './components/DisplayCar.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="/car/:id" component={DisplayCar}/> <Route exact path="/cars" component={DisplayAllCars}/> <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>}/>
The code that points to the DisplayCar component includes an id. This allows us to pass a variable (parameter) to the DisplayCar 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="/car/:id" component={DisplayCar}/>
The id included in the path will automatically be included as part of the props object that is passed to the DisplayCar component. Within the DisplayCar component code, the id can be accessed using the code below.
this.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, {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 DisplayAllCars from './components/DisplayAllCars.js' export default class App extends Component { render() { return ( <BrowserRouter> <div> <header> <Link to="/home">Home</Link> <Link to="/about">About</Link> <Link to="/cars">Cars</Link> </header> <Switch> <Route exact path="/" component={Home}/> <Route exact path="/home" component={Home}/> <Route exact path="/about" component={About}/> <Route exact path="/cars" component={DisplayAllCars}/> <Route path="*" component={() => <h3>Invalid URL. Webpage does not exist</h3>}/> </Switch> <footer> Footer </footer> </div> </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, {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.
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.
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.
Open the react_app 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 in the basic app lesson on the previous webpage, 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 "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="/cars" 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="/cars" 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="/cars" 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="/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={DisplayAllCars}/> </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.