Skip to main content
Home / Articles /

The Hooks of React Router

React Router 5 embraces the power of hooks and has introduced four different hooks to help with routing. You will find this article useful if you are looking for a quick primer on the new patterns of React Router. But before we look at hooks, we will start off with a new route rendering pattern.

Before React Router 5

// When you wanted to render the route and get router props for component
<Route path="/" component={Home} />


// Or when you wanted to pass extra props
<Route path="/" render={({ match }) => <Profile match={match} mine={true} />}>

When using the component syntax, route props (match, location and history) are implicitly being passed on to the component. But it has to be changed to render once you have extra props to pass to the component. Note that adding an inline function to the component syntax would lead to the component re-mounting on every render.

After React Router 5

<Route path="/">
  <Home />
</Route>

Note that there is no implicit passing of any props to the Home component. But without changing anything with the Route itself, you can add any extra props to the Home component. You can no longer make the mistake of re-mounting the component on every render and that’s the best kind of API.

But now route props are not being passed implicitly, so how do we access match, history or location? Do we have to wrap all components with withRouter? That is where the hooks steps in.

Note that hooks were introduced in 16.8 version of React, so you need to be above that version to use them.

useHistory

  • Provides access to the history prop in React Router
  • Refers to the history package dependency that the router uses
  • A primary use case would be for programmatic routing with functions, like push, replace, etc.
import { useHistory } from 'react-router-dom';

function Home() {
  const history = useHistory();
  return <button onClick={() => history.push('/profile')}>Profile</button>;
}

useLocation

  • Provides access to the location prop in React Router
  • It is similar to window.location in the browser itself, but this is accessible everywhere as it represents the Router state and location.
  • A primary use case for this would be to access the query params or the complete route string.
import { useLocation } from 'react-router-dom';

function Profile() {
  const location = useLocation();
  useEffect(() => {
    const currentPath = location.pathname;
    const searchParams = new URLSearchParams(location.search);
  }, [location]);
  return <p>Profile</p>;
}

Since the location property is immutable, useEffect will call the function every time the route changes, making it perfect to operate on search parameters or current path.

useParams

  • Provides access to search parameters in the URL
  • This was possible earlier only using match.params.
import { useParams, Route } from 'react-router-dom';

function Profile() {
  const { name } = useParams();
  return <p>{name}'s Profile</p>;
}

function Dashboard() {
  return (
    <>
      <nav>
        <Link to={`/profile/ann`}>Ann's Profile</Link>
      </nav>
      <main>
        <Route path="/profile/:name">
          <Profile />
        </Route>
      </main>
    </>
  );
}

useRouteMatch

  • Provides access to the match object
  • If it is provided with no arguments, it returns the closest match in the component or its parents.
  • A primary use case would be to construct nested paths.
import { useRouteMatch, Route } from 'react-router-dom';

function Auth() {
  const match = useRouteMatch();
  return (
    <>
      <Route path={`${match.url}/login`}>
        <Login />
      </Route>
      <Route path={`${match.url}/register`}>
        <Register />
      </Route>
    </>
  );
}

You can also use useRouteMatch to access a match without rendering a Route. This is done by passing it the location argument.

For example, consider that you need your own profile to be rendered at /profile and somebody else’s profile if the URL contains the name of the person /profile/dan or /profile/ann. Without using the hook, you would either write a Switch, list both routes and customize them with props. But now, using the hook we can do this:

import {
  Route,
  BrowserRouter as Router,
  Link,
  useRouteMatch,
} from 'react-router-dom';

function Profile() {
  const match = useRouteMatch('/profile/:name');

  return match ? <p>{match.params.name}'s Profile</p> : <p>My own profile</p>;
}

export default function App() {
  return (
    <Router>
      <nav>
        <Link to="/profile">My Profile</Link>
        <br />
        <Link to={`/profile/ann`}>Ann's Profile</Link>
      </nav>
      <Route path="/profile">
        <Profile />
      </Route>
    </Router>
  );
}

You can also use all the props on Route like exact or sensitive as an object with useRouteMatch.

Wrapping up

The hooks and explicit Route comes with a hidden advantage in itself. After teaching these techniques at multiple workshops, I have come to the realization that these help avoid a lot of confusion and intricacies that came with the earlier patterns. There are suddenly far fewer unforced errors. They will surely help make your router code more maintainable and you will find it way easier to upgrade to new React Router versions.