React Router Hooks: Exploring How They Work
The React Router is not the same as the router that directs networking data — luckily! However, there are a lot of similarities between a networking router and the React Router. The React Router helps us to navigate users to the correct component. For instance, we can use client-side routing to build a single-page application (SPA) that allows navigation between different pages without refreshing the browser.
In other words, the React Router keeps your UI in sync with the URL. It has a simple API with powerful features like lazy code loading, dynamic route matching, and location transition handling built right in.
But, there’s much more to discover. This article will dive into React Router hooks. These hooks allow developers to write much cleaner code as they don’t have to write all the boilerplate code like a class component. We can access several hooks, out of the box, such as useHistory
, useLocation
, useParams
, and useRouteMatch
. For example, the useHistory
hook gives us access to the history
object to handle route changes.
This article will cover the following sections:
- A basic example of the React Router to get you started
- Examples of each React Router hook to understand its use
1. Getting Started: Setting Up a React Project
To get started, we need to create a new React app by running the following command in your terminal:
npx create-react-app react-router-tutorial
This command creates a new project called react-router-tutorial
.
Now, let’s modify the App.js
file.
import React, { Fragment } from "react";
import "./index.css"
export default function App() {
return (
<main>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/shop">Shop</a></li>
</ul>
</nav>
</main>
);
}
// Home Page
const Home = () => (
<Fragment>
<h1>Home</h1>
<LoremText />
</Fragment>
);
// About Page
const About = () => (
<Fragment>
<h1>About</h1>
<LoremText />
</Fragment>
);
// Shop Page
const Shop = () => (
<Fragment>
<h1>Shop</h1>
<LoremText />
</Fragment>
);
const LoremText = () => (
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
)
Our code defines three different pages Home, About, and Shop. Moreover, we’ve added a <nav>
element that takes care of the navigation. If you click on the /about
link, we want to navigate to the About page. To do so, we need to add the react-router-dom
dependency to our project. Execute the following command in your terminal:
npm install --save react-router-dom
Now, let’s configure our React Router.
2. Setting Up the React Router
First, we need to import the Router
object into our App.js
file. Once that is done, we can use the Router
object to wrap the entire application as we want routing to be available everywhere in our application.
Note that we’ve also refactored the <a>
tags to the <Link>
tags React Router offers. First of all, they will save you problems with relative versus absolute paths but more importantly the <Link>
tag doesn’t trigger a page refresh while the <a>
tag naturally does.
On top of that, we import the Route
object to define the mapping between path and component. For instance, the path /shop
should load the Shop
component.
import React, { Fragment } from "react";
import "./index.css"
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
export default function App() {
return (
<Router>
<main>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/shop">Shop</Link></li>
</ul>
</nav>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/shop">
<Shop />
</Route>
</Switch>
</main>
</Router>
);
}
// Home Page
const Home = () => (
<Fragment>
<h1>Home</h1>
<LoremText />
</Fragment>
);
// About Page
const About = () => (
<Fragment>
<h1>About</h1>
<LoremText />
</Fragment>
);
// Shop Page
const Shop = () => (
<Fragment>
<h1>Shop</h1>
<LoremText />
</Fragment>
);
const LoremText = () => (
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
)
Let’s check if the setup works for you by first building the project:
npm run build`
Next, let’s start the project and open your browser at http://localhost:3000
.
npm run start
You should see the navigation, a title, and the Lorem Ipsum text. Make sure to verify if the navigation works for you.
If everything works, let’s explore React Router hooks!
3. Configuring React Router Hooks
Let’s explore how we can use the different React Router hooks.
useHistory
The first hook useHistory
allows us to access the history
object. We can then call methods on the history
object like goBack
or push
. The goBack
method allows us to route the user to the previous route in the history stack. For instance, if the user navigates from the Home
page to the Shop
page and then clicks the button to go back, they’ll be redirected to the Home
page again.
On the other hand, we can add new entries to the history stack and make the user navigate to this route by using the push
method.
Here’s the final code that implements a “Go Back” button for the About
page. Don’t forget to import the useHistory
hook from react-router
.
import React, { Fragment } from "react";
import "./index.css"
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import { useHistory } from "react-router";
// About Page
const About = () => {
const hist = useHistory();
return (
<div>
<h1>About</h1>
<button onClick={() => hist.goBack()}>Go Back</button>
<LoremText />
</div>
);
};
export default function App() {
return (
<Router>
<main>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/shop">Shop</Link></li>
</ul>
</nav>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/shop">
<Shop />
</Route>
</Switch>
</main>
</Router>
);
}
// Home Page
const Home = () => (
<Fragment>
<h1>Home</h1>
<LoremText />
</Fragment>
);
// Shop Page
const Shop = () => (
<Fragment>
<h1>Shop</h1>
<LoremText />
</Fragment>
);
const LoremText = () => (
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
)
useParams
From the React documentation, we can find the following description for useParams
:
useParams
returns an object of key/value pairs of URL parameters. Use it to accessmatch.params
of the current<Route>
.
It’s a much cleaner way to access URL parameters. To illustrate this, let’s modify the shop page to accept URL parameters and print the received parameter to the screen.
The first thing we did is importing the useParams
hook. Next, we have to change the Shop
route to accept a URL parameter. By default, we will show the first product ID when clicking the Link
tag from the navigation. As you can see, we’ve modified the path for the Shop
route to /shop/:id
.
<Router>
<main>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/shop/1">Shop</Link></li>
</ul>
</nav>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/shop/:id">
<Shop />
</Route>
</Switch>
</main>
</Router>
Now, we can access the id
parameter via the params
object that is returned by the useParams
hook.
Also, we’ve added the possibility to navigate to the following product ID using a button with the text Next product
. Here, we use the push
method exposed by the history
object from the previous example to push a new entry to the history
stack. If you use the Go Back
button, you’ll notice that you are redirected to the previous product IDs. Try it!
import React, { Fragment } from "react";
import "./index.css"
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import { useHistory, useParams } from "react-router";
// About Page
const About = () => {
const hist = useHistory();
return (
<div>
<h1>About</h1>
<button onClick={() => hist.goBack()}>Go Back</button>
<LoremText />
</div>
);
};
const Shop = () => {
const params = useParams();
const current = params.id;
const next = Number(current) + 1;
const hist = useHistory();
return (
<div>
<h1>Shop</h1>
<p>You requested item with ID: {current}</p>
<button onClick={() => hist.goBack()}>Go Back</button>
<button onClick={() => hist.push(`/shop/${next}`)}>Next product</button>
</div>
);
};
export default function App() {
return (
<Router>
<main>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/shop/1">Shop</Link></li>
</ul>
</nav>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/shop/:id">
<Shop />
</Route>
</Switch>
</main>
</Router>
);
}
// Home Page
const Home = () => (
<Fragment>
<h1>Home</h1>
<LoremText />
</Fragment>
);
const LoremText = () => (
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
)
That’s it for the useParams
React Router hook!
useLocation
The useLocation
hook returns the location object that represents the current URL. You can think about it like a useState
that returns a new location whenever the URL changes. You can use it, for instance, to trigger a new page view event for a web analytics tool.
Here’s a modified example of the About
component that prints the pathname. Every time you visit the About
page, the path /about
will be printed to the console.
import { useHistory, useLocation, useParams } from "react-router";
// About Page
const About = () => {
const hist = useHistory();
const location = useLocation();
// Fictive call to Google Analytics
// ga.send(["pageview", location.pathname])
console.log(location.pathname);
return (
<div>
<h1>About</h1>
<button onClick={() => hist.goBack()}>Go Back</button>
<LoremText />
</div>
);
};
Furthermore, we can even retrieve the query parameters via the useLocation
router hook. The returned location
object contains a search
field which contains the query string. Now, if you use simple key=value
query strings and you do not need to support IE 11, you can use the browser’s built-in URLSearchParams
API. This API exposes several methods for verifying if the query string contains a certain key (using has
) but we want to retrieve a query parameter using the get
function.
Let’s modify the Link
for the About page in our router from /about
to /about?param=text
.
<Link to="/about?param=text">About</Link>
Now, let’s retrieve the value for the parameter called param
using the below code:
console.log(new URLSearchParams(location.search).get("param")); // result: "text"
The code for the About
function looks like this.
// About Page
const About = () => {
const hist = useHistory();
const location = useLocation();
// Fictive call to Google Analytics
// ga.send(["pageview", location.pathname])
console.log(location.pathname);
console.log(location.search);
console.log(new URLSearchParams(location.search).get("param")); // "text"
return (
<div>
<h1>About</h1>
<button onClick={() => hist.goBack()}>Go Back</button>
<LoremText />
</div>
);
};
useRouteMatch
The React Router docs define the useRouteMatch
as follows:
The
useRouteMatch
hook attempts to match the current URL in the same way that a<Route>
would. It’s mostly useful for getting access to the match data without actually rendering a<Route>
.
In our example, we will use the route match pattern for the Shop page. If the URL doesn’t contain an ID (/shop
) then we render all products. Else, if it contains an ID (/shop/:id
) we render the specific product. Without the route match hook, we need to use a Switch statement to render both pages. Now, we can check which route matches and render the correct page.
Make sure first to import the useRouteMatch
hook. Then we pass the path we want to verify to the useRouteMatch
hook: /shop/:id
. If the route matches, we render the product page. We can access the product ID via routeMatch.params.id
.
const Shop = () => {
const hist = useHistory();
const routeMatch = useRouteMatch("/shop/:id");
// Use match to render the correct page
return routeMatch ? (
<div>
<h1>Shop</h1>
<p>You requested item with ID: {routeMatch.params.id}</p>
<button onClick={() => hist.goBack()}>Go Back</button>
</div>
) : (
<div>
<h1>Shop</h1>
<p>All products</p>
<br />
<button onClick={() => hist.goBack()}>Go Back</button>
</div>
);
};
Now, let’s modify the Router to include an extra link that redirects us to the Shop overview page. Both the /shop
and /shop/1
links are connected to the <Shop />
component, without using a Switch
statement. A single Route
element is sufficient, with a path set to /shop
.
export default function App() {
return (
<Router>
<main>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about?param=text">About</Link>
</li>
<li>
<Link to="/shop">Shop</Link>
</li>
<li>
<Link to="/shop/1">Product 1</Link>
</li>
</ul>
</nav>
<Route path="/shop">
<Shop />
</Route>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
</Switch>
</main>
</Router>
);
}
Open Source Session Replay
OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data.
Start enjoying your debugging experience - start using OpenReplay for free.
That’s it!
If you want to learn more about React Router hooks, make sure to check out the code examples in the React Router documentation.
→ The completed code can be found at CodeSandBox.io.