Back

React Router Hooks: Exploring How They Work

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.

Home page after executing npm run start

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 access match.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.

replayer.png

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.