A bluffer’s guide to React Router v4
The important things you can quickly learn to be informed and confident with using and conversing on React Router v4
What is React Router?
React Router is a client-side router (CSR) for use with React projects (I know, duh right?). It provides routing which is the fancy term for the rendering of different components depending on URL paths within a React web application.
How does one install and use?
Run the following command in your project to save react-router-dom
as a project dependency:
npm i -S react-router-dom
Using the ES2015 syntax, you can import React Router into your React components using:
import * from 'react-router-dom'
Setting up your basic routes
Link
components can be thought of as your typical anchor links that, when clicked, redirect the user to the path specified in its to
property.
Route
components can be thought of as the controller for the render. When you see these components, just think this is what they say:
WHEN I see the URL as what is specified in my path
property, THEN I will render the component listed in my component
or render
property.
The basic example below is mostly lifted from ReactTraining’s basic example:
Nesting Routes
Nesting routes is exactly the same as creating high-level routes, except for defining a BrowserRouter
component (as your nested routes are composed within the high-level BrowserRouter
component anyway). It simply needs more Link
and Route
components. We can indefinitely nest further routes within any other nested routes.
Passing URL parameters
In the previous example, we had different components defined for each topic in nested routes. In the following example, we have a single Topic
component that is rendered for the three different routes. The Topic
component dynamically renders the topicId
, which is passed as a property by the Route
, and its value defined as part of the URL using the :
.
When a Route
defines a component to render, it passes a match
object to its props (along with location
and history
, but unimportant for now). This match
object has a params
object which contains any variables passed by Route
defined using the :
notation in the path
URL (aka Route Parameter).
In this way, we can cut down on separately creating components for each Link
and instead make one re-usable component rendered with the information passed to it.
Avoiding hard-coding nested links
When creating nested links, our Link
component still needs to refer to the entire URL path instead of the location it’s really concerned with. This means that nested links would have hard-coded locations to its parent links, which isn’t great when a name change occurs and a big renaming effort is required.
Instead, using the match
object passed by Route
, we can dynamically refer to it’s location and use that to avoid hard-coding.
For example:<Route path="/topics" component={Topics}/>
passes a match
object with a url
property with the value "/topics"
. Topics
, via its props, can reuse the match.url
when defining its nested links.
Avoiding ambiguous matches
By default, when you specify Route
components, each matching path will be rendered inclusively. Using URL parameters, this becomes problematic. As the parameter effectively acts as a wildcard (so any text is found as matching), you will find that when these are mixed with hard-coded routes, they both will display. Using exact
won’t help either.
React Router’s solution is the Switch
component. The Switch
component will render the child Route
component on the first matching path exclusively. This means that if all hard-coded routes are specified first, then these will be rendered only.
Multiple Route Rendering
There are times when you don’t want ambiguous matching of Route
components, but there will be times when you do.
Remembering that we can think of Route
as a simple “WHEN I see this path, THEN render this component”, which means we can have multiple Route
components matching a single page, but providing different content.
In the below example, we use one component to pass the URL parameter to show the user their current path, and another component that renders with the content component. That means two different components are rendered for the one URL path.
Rendering from Route directly
A Route
component can be passed a component
to render if one is available. It can also render a component directly using the render
property.
<Route exact path={url} render={
() => <h3>Please select a topic</h3>
} />
Passing properties to a component using Route
URL passed parameters are fine, but what about passing in properties that require more data to components, such as objects? Route
doesn’t allow you to append properties it doesn’t recognize.
<Route exact path="/" component={Home} doSomething={() => "doSomething" } /> // doesn't work
However what can be done to pass properties is to use Route
render method.
<Route exact path="/" render={(props) => <Home {...props} doSomething={() => "doSomething"} />
Passing properties to a component using Link
You can also pass properties to a component via the Link
component. Instead of passing in a String to the to
property, we can pass in an object instead. On this object, we can declare the pathname
representing to URL we want to navigate to. We also declare a state
object that contains any custom properties we want. These properties are contained on the location
object (in location.state
).
In the below example (contrived, I know…), we pass in a message property to display on a page.
Redirection — Using Redirect
You can use the Redirect
component to redirect the users’ immediate URL to another.
In the below example, we see a redirect for a user depending on whether the isAuthenticated
state on component RedirectExample
is true or false, appropriately redirecting if they’re logged in (to /home) or logged out (to /dashboard):
Redirection — Using withRouter()
Another way to redirect is by using the higher-order component withRouter
. This allows you to pass the properties of Route
(match
, location
, and importantly in this example history
) to components that aren’t rendered via the typical Route
component. We do this by wrapping our exported component with the withRouter
.
Why is having history
important? We can use history
to force redirection by pushing a URL to the history
object.
There are caveats to this way of routing which I don’t detail (see the withRouter documentation). Also, the exported component must still be composed within another component that is rendering withinBrowserRouter
(which I don’t show in this example).
Default Route Component
There will be times when a Link
may refer to a URL that has no corresponding <Route path='/something' component={Something}/>
. Or a user will type an incorrect URL in the browser bar.
When that happens, all we will have is a non-responsive page or link where nothing happens (which is not as bad as actually being navigated to a non-existent page).
Most times we want to at least show the user we can’t find their content, maybe with a witty image like Github’s 404 page. In this case, you’ll want a default component, also known as a no-match or catch-all component.
When a user clicks a link (or indeed types something incorrectly in the browser navigation bar), so long as there are no other matching components, we will be directed to the default component to be rendered.
Note the use of Switch
(ambiguous matching with URL parameters). As this Route
has no path, it will be effectively always rendered. We require a Switch
to only render if it cannot find any other matching Route
path’s.
Custom Links
The Link
component at its core renders an anchor element for whatever is passed as its to
property. There are times when we would want customizations made to the Link component without having to have these customizations everywhere. React Router allows a means of doing this is by creating a custom link (see React Router training guide for more info — we use their example more or less below).
For a custom link, one is essentially wrapping the existing Link
component inside a custom component, and providing additional information, not unlike the Higher Order Component pattern.
In our example, we only show the links for the pages that aren’t displayed. For our CustomLink
component to have the knowledge of what page is currently displayed, we need to wrap the Link
component in a Route
component so that we can pass the match
object that comes with React Router’s Route
. We pass this wrapping our Link
component as a child to the Route
component.
Route, if you remember, simply checks the current path and when we match, will render “something” (either defined by the component
/render
properties or as as a child of Route
— such as our Link component).
We subvert this slightly with an equality check to say if we don’t find a match
object (if our current path doesn’t match what’s defined in the path
property in the Route
declared in CustomLink
), then render a Link
, otherwise render nothing.
Parsing Query Strings
Query strings are parameters in the URL that look like variables and are prefixed by the question mark (such as www.example.com/search?name=Greg
).
I’m going to rip the band-aid off quick — React Router doesn’t come with a way to parse query strings 😮. It does, however, locate query strings as a String in its passed properties, within location.search
, wherein the above example would be defined as search: "?name=Greg"
.
So what to do? There are a number of ways to solve this problem, including reinventing the wheel. I would like to anecdotally highlight the npm package query string as a solution which I’ve used and has become my de facto query string parser.
Prompting users when transitioning
React Router v4 comes with a Prompt
component, which as you can guess, displays a prompt to the user. This is useful as UX feature for users when they are warned of potentially losing form data if they transition from the current form page.
The basic pattern in implementing a navigational prompt is to have a boolean state property which decides whether they should be prompted or not. This state is updated via the form based on whether there are values selected for any form field. Within the form, you render a Prompt
component, passing in two properties: a when
(which is your boolean state set earlier) and a message
to display.
Below is an example (it mostly follows the example from ReactTraining prevent transitions):
Summary
In the article, you will have learned all the basics to use React Router in your web applications. You should be even able to converse with your many friends and family about the joys of React Routing.
If you liked this article, please share your like with a friendly clap.
If you didn’t like this article and would like to register your resentment, you can do so by giving a hateful clap.
The opinions expressed in this publication are those of the author. They do not purport to reflect the opinions or views of any organization or business that the author may be connected to.