How to create a simple React router hook using React's built-in functionality (and React's own code)

Published on Aug. 22, 2023, 12:12 p.m.

If you use ReactRouter you might have noticed they recently added a number of useful hooks.But let’s see if we can make it even simpler by wrapping them up into a single useRouter hook.In this recipe we show how easy it is to compose multiple hooks.It makes a lot of sense using only the hook you need can minimize unnecessary re-renders.That said, sometimes you want a simpler developer experience .

import { useMemo } from "react";
import {
  useParams,
  useLocation,
  useHistory,
  useRouteMatch,
} from "react-router-dom";
import queryString from "query-string";
// Usage
function MyComponent() {
  // Get the router object
  const router = useRouter();
  // Get value from query string (?postId=123) or route param (/:postId)
  console.log(router.query.postId);
  // Get current pathname
  console.log(router.pathname);
  // Navigate with router.push()
  return <button onClick={(e) => router.push("/about")}>About</button>;
}
// Hook
export function useRouter() {
  const params = useParams();
  const location = useLocation();
  const history = useHistory();
  const match = useRouteMatch();
  // Return our custom router object
  // Memoize so that a new object is only returned if something changes
  return useMemo(() => {
    return {
      // For convenience add push(), replace(), pathname at top level
      push: history.push,
      replace: history.replace,
      pathname: location.pathname,
      // Merge params and parsed query string into single "query" object
      // so that they can be used interchangeably.
      // Example: /:topic?sort=popular -> { topic: "react", sort: "popular" }
      query: {
        ...queryString.parse(location.search), // Convert string to object
        ...params,
      },
      // Include match, location, history objects so we have
      // access to extra React Router functionality if needed.
      match,
      location,
      history,
    };
  }, [params, match, location, history]);
}