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?
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:
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 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
.
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.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:
Then you should be fine. So this will always work regardless of your environment (dev/prod):
matchPath(props.location.pathname, {
path: '/business',
exact: true,
});
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With