Skip to content

useMatch's memo is easy to break by accident using a PathPattern #12978

@epmatsw

Description

@epmatsw

I'm using React Router as a...

library

Reproduction

I promise I tried to make a StackBlitz for this, but it really doesn't like demos that trigger infinite loops 😂

This is kind of a toy example, but if you pass a PathPattern object to useMatch, its memoization starts to fail. That's sneaky, because going from (working) code like this:

  const [count, setCount] = useState(0);
  const match = useMatch('test');
  useEffect(() => setCount((c) => c + 1), [match]);

to this:

  const [count, setCount] = useState(0);
  const match = useMatch({
    path: 'test',
    caseSensitive: true,
  });
  useEffect(() => setCount((c) => c + 1), [match]);

introduces an infinite loop.

I think that the memo here:

return React.useMemo(
() => matchPath<ParamKey, Path>(pattern, decodePath(pathname)),
[pathname, pattern]

could be tweaked a bit to pull the values off of the passed in object (if it's an object) instead of forcing each caller to add an additional useMemo to get memoization to work.

System Info

Binaries:
    Node: 23.7.0 - ~/.nvm/versions/node/v23.7.0/bin/node
    Yarn: 4.6.0 - /opt/homebrew/bin/yarn
    npm: 10.9.2 - ~/.nvm/versions/node/v23.7.0/bin/npm
    bun: 1.2.1 - ~/.bun/bin/bun
    Watchman: 2024.11.04.00 - /opt/homebrew/bin/watchman
  Browsers:
    Chrome Canary: 135.0.7003.0
    Safari Technology Preview: 18.0

Used Package Manager

npm

Expected Behavior

useMatch returns a memoized object when feasible.

Actual Behavior

When you pass in a PathPattern, useMatch's result is no longer memoized.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions