Search code examples
reactjstypescriptreact-routerreact-router-dom

useMatch for multilpe patterns to test on in react-router-dom


I have the following array:

const tabsList = [
  { 
    pathPattern: 'users/'
    label: 'Manage users'
  },
  {
    pathPattern: 'users/:id',
    label: 'Edit user profile'
  }
] 

I need a method from react-router-dom to tell me which of the above array entries matches the current pathname.

Based on that, I will be able to color the active list item in my component styles.

I found the method useMatch() which looks like does almost what I want, however, it does not accept an array, it only accepts one string pattern to use for comparison.

// example: current path is /users/82374023854321
const isAllUsersMatch = useMatch('/users') // null
const isUserIdMatch = useMatch('/users/:id') // { ...PathMatch<string> } 

it works, but it's not good, I have to create a separate variable like that for every item in the array.

Is there something like the following in react-router-dom?

const tabsList = [
  { 
    pathPattern: 'users/'
    label: 'Manage users'
  },
  {
    pathPattern: 'users/:id',
    label: 'Edit user profile'
  }
] 
const activeListItem = tabsList.some((listItem)=> doesItMatch(listItem.pathPattern))

I would have done this with the hook, but in react you can't use a hook inside a callback, so the following would not work:

// const activeListItem = tabsList.some((listItem)=> doesItMatch(listItem.pathPattern))
const activeListItem = tabsList.some((listItem)=> useMatch(listItem.pathPattern)) // ERROR

Solution

  • You are on the right track iterating over the tabsList array. If all you need is to match the current path to one in a configuration object/array, then instead of using the useMatch hook, which doesn't work in a nested callback function, you can use the matchPath utility function that the useMatch hook uses directly.

    import { matchPath, useLocation } from "react-router-dom";
    
    const tabsList = [
      {
        pathPattern: "users/",
        label: "Manage users"
      },
      {
        pathPattern: "users/:id",
        label: "Edit user profile"
      }
    ];
    
    ...
    
    const { pathname } = useLocation();
    const activeListItem = tabsList.find(({ pathPattern }) =>
      matchPath(pathPattern, pathname)
    );
    

    And the above abstracted into a custom hook:

    const useTabsListMatches = (tabsList = []) => {
      const { pathname } = useLocation();
      return tabsList.find(({ pathPattern }) =>
        matchPath(pathPattern, pathname)
      );
    }
    
    const activeListItem = useTabsListMatches(tabsList);