Nathan Sebhastian A senior software developer with experience in building fullstack JavaScript app with React and Express. You can find me online at sebhastian.com.

Cleaner components with React Router Hooks

4 min read 1121

React Router Hooks Cleaner Component

Editor’s note: This post was last updated 07 March 2022 to reflect information on route render methods that had become outdated.

React Router is a lightweight library that allows you to manage and handle routing for your React application. When it comes to building single page apps with React and React Native, React Router comes in handy.

Since the introduction of functional components, React has advanced a lot, for example, through the introduction of Hooks. Instead of normal class-based methods, Hooks allow the package to work well with functional components, and many packages are migrating in this direction.

As a result, we see React Router Hooks. In this article, we’ll review three React Hooks that will make it easier to work with React Router. Let’s get started!

Table of contents

The useParams Hook

The useParams Hook will return an object of key/value pairs from your application URL that is set to be dynamic. In a complex application, it’s common to have many dynamic navigation links. For example, you may have a /post/:id URL that also initiates a fetch process to your application’s backend. In this case, the most common React Router pattern would be to use a component prop:

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/post/hello-world">First Post</Link>
            </li>
          </ul>
        </nav>
        <Routes>
          <Route path="/post/:slug" element={<Post />} />
          <Route path="/">
            <Home />
          </Route>
        </Routes>
      </div>
    </Router>
  );
}

By passing the Post component into the /post/:numberRoute component, you can extract the params object from the match prop that React Router passes into the Post component:

// Old way to fetch parameters
function Post({ match }) {
  let params = match.params;
  return (
    <div>
      In React Router v4, you get parameters from the props. Current parameter
      is <strong>{params.slug}</strong>
    </div>
  );
}

Although the method above works, it’s quite cumbersome if you have a large application with many dynamic routes. You have to keep track of which Route components need component props and which ones don’t. In addition, since the match object is passed from Route into the rendered component, you’ll need to pass the dynamic routes along to components further down the DOM tree.

In this type of situation, the useParams Hook really shines. It’s a neat helper function that gives you the parameters of the current route so you don’t have to use the component props pattern:

<Switch>
  <Route path="/post/:slug" element={<Post />} />
  <Route path="/users/:id/:hash" element={<Users />} />
  <Route path="/" element={<Home />} />
</Switch>

function Users() {
  let params = useParams();
  return (
    <div>
      In React Router v5, You can use hooks to get parameters.
      <br />
      Current id parameter is <strong>{params.id}</strong>
      <br />
      Current hash parameter is <strong>{params.hash}</strong>
    </div>
  );
}

If a child component of Users needs access to the parameters, you can simply call useParams() there as well.

The useLocation Hook

In React Router v4.x, just like fetching parameters, you had to use the component props pattern to gain access to a location object:

<Route path="/post/:number" component={Post} />

function Post(props) {
  return (
    <div>
      In React Router v4, you get the location object from props. <br />
      Current pathname: <strong>{props.location.pathname}</strong>
    </div>
  );
}

However, with React Router v6.x, you can call the useLocation Hook to get the location object from React Router:

<Route path="/users/:id/:password" element={<Users />} />

// new way to fetch location with hooks
function Users() {
  let location = useLocation();
  return (
    <div>
      In React Router v5, You can use hooks to get location object.
      <br />
      Current pathname: <strong>{location.pathname}</strong>
    </div>
  );
}

The useNavigate Hook

From the previous section, we have one less reason to use component props. But don’t you still need to use the component or render pattern to get the history object?

The history object is the last reason why you need to use component or render props pattern:

<Route path="/post/:slug" component={Post} />

// Old way to fetch history
function Post(props) {
  return (
    <div>
      In React Router v4, you get the history object from props. <br />
      <button type="button" onClick={() => props.history.goBack()}>
        Go back
      </button>
    </div>
  );
}

With the useNavigate Hook, you can get the same history object without needing the Route component to pass it down:

<Route path="/users/:id/:hash" element={<Users />}>

// new way to fetch history with hooks
function Users() {
  let navigate = useNavigate();
  return (
    <div>
      In React Router v5, You can use hooks to get navigate function to move around in the app.
      <br />
      <button type="button" onClick={() => navigate(-1)}>
        Go back
      </button>
    </div>
  );
}

React Router v6 doesn’t provide the useHistory Hook. Instead, we navigate with the useNavigate Hook. In the example above, we are using navigate(-1) to go back to the previous page. -1 determines the number of pages you want to go back. For example, -2 would go back two pages, and so on.

Refactor your component with React Router Hooks

The React Router team harnessed the power of Hooks and implemented it to share logic across components without the need to pass it down from from the top of the tree.If you’d like to refactor your component with these new Hooks, you can start by updating components that use match, location, or history objects:

// before
function userComponent({ match, location, history }) {
  let { slug } = match.params
  // ...
}

// after
function userComponent() {
  let { slug } = useParams()
  let location = useLocation()
  // ...
}

After that, you can update the weird-looking <Route> components you might have in your navigation component:

// before
<Switch>
  <Route path="/user/:id" component={userComponent} />
</Switch>

// after
<Switch>
  <Route path="/user/:id" element={<userComponent />}>
</Switch>

What do you think about React Router Hooks? Please leave a comment below if you have any questions. Happy coding!

Get setup with LogRocket's modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.
  3. $ npm i --save logrocket 

    // Code:

    import LogRocket from 'logrocket';
    LogRocket.init('app/id');
    Add to your HTML:

    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
  4. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • ngrx middleware
    • Vuex plugin
Get started now
Nathan Sebhastian A senior software developer with experience in building fullstack JavaScript app with React and Express. You can find me online at sebhastian.com.

Leave a Reply