Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Basename does not seem to be taken into account with matchPath in react router

I am looking into a bug we have with react-router-dom version 5.0.0 and the active class not being applied to the active link.

It works in development when we have a basename of / but in our dev environment, the basename is set to `/some/basename/'.

If I call this matchPath directly like this:

matchPath("/some/basename/business", {
  exact: true,
  location: undefined,
  path: "\/business",
  strict: undefined
})

Then it returns null, if I call matchpath like this, it gets a match:

matchPath("/business", {
  exact: true,
  location: undefined,
  path: "\/business",
  strict: undefined
})

So it looks like matchPath is not using the basename because after trying to debug the code I see that matchPath calls pathToRegexp which creates this regex:

/^\/business\/?$/i

The code only appears to be using the path property and not the location.pathname.

I tried to implement my own isActive method to log what was going on:

const isActive = (match: Match<any>, historyLocation: Location) =>  {
  const basename = (window.env.REACT_APP_BASE_URI || "/") === "/" ? "" : window.env.REACT_APP_BASE_URI;
  const fullUrl = `${basename.replace(/^(.+?)\/*?$/, "$1")}${historyLocation.pathname}`;

  console.log("----------------------");
  console.log({
    basename,
    fullUrl,
    historyLocation,
    historyLocationPathName: historyLocation.pathname,
    location: window.location.pathname,
    match,
  });
  console.log("----------------------");

  if (!match) {
    return;
  }

  return window.location.pathname.startsWith(`${fullUrl}`);
};

match is always null in the environment but works fine in development.

Why would this be?

like image 685
dagda1 Avatar asked May 25 '19 06:05

dagda1


1 Answers

HTML Concept

This is actually one of the great advantages of react-router, because in development stage you have no idea, where the project is going to be deployed. It might be deployed on a:

  • Primary Domain
  • Sub Domain
  • Sub Directory

So the best approach is to provide the define all the relative URLs in a base URL agnostic way. basename in React-Router is not actually something new, and we have the <base> head tag in HTML:

<head>
   <base href="https://www.yoursite.com/some/basename/" target="https://www.yoursite.com/other/">
</head>

This tag means that all the relative URLs in the page will have https://www.yoursite.com/some/basename/ added to them, such as:

<img src="header.jpg">

Will be the same as

<img src="https://www.yoursite.com/some/basename/header.jpg">

and all relative links and form actions will also have href attached to them.


React Router

React-Router basically uses this concept (however, not base tag), so when you set basename in the main ` the following elements will know nothing about basename:

  • <Route>
  • <Link>
  • <Redirect>
  • matchPath

so when you have a Link like this:

<Link to="/page2" />

it will actually redirect to /some/basename/page2.


basename

  • React-Router Docs suggests to that A properly formatted basename should have a leading slash, but no trailing slash. (eg. /some/business) . However, in most of the modern browsers, it doesn't matter at all.

matchPath

Now let's talk about the matchPath. As I said before, matchPath does not know anything about the basename, so you have these in mind:

  • make sure that the input URL does not have basename inside it.
  • Do not use window.location.pathname, instead use the React-Router location props

Then you should be fine. So this will always work regardless of your environment (dev/prod):

matchPath(props.location.pathname, {
    path: '/business',
    exact: true,
});

Notes

If with all these in mind, you run into some problem throughout your project (including all links, all redirects, all routes, and also matchPath), then your base basename is set wrong in the production environment.

like image 88
Moji Izadmehr Avatar answered Oct 16 '22 06:10

Moji Izadmehr