I am using a library called Material-UI, to build up my app layout. I am using its <ListItemButton>
component inside the sidenav of my app. Now. this component <ListItemButton>
is not a link, its a button. but it has some styles, you can see how it looks here, click me. This component accepts a component
prop, what this component
prop does is that it basically allows you to render something inside of it, for instance, I can give it <div/>
, I can give it <a/>
I can give it<b/>
etc etc, but I can also give it a React component. and that's what I am doing here. I am giving it the React router <NavLink>
component.
Now. this is so easy, just give it the <NavLink>
component and its going to work. But, unfortunately, it doesn't allow you to write <NavLink>
as <NavLink>
, you must write it as NavLink
without the brackets, therefore, I can not pass any arguments to it.
Luckily, the way this Material-UI works, is that when it sees a prop inside one of its components such as the <ListItemButton>
component that it doesn't understand, it automatically passes that prop to the child component. so in our case, if we wrote a prop that is not inside the <ListItemButton>
documentation, it's going to pass it to the NavLink
component.
hence, we can pass the to
prop. as follows:
<ListItemButton to="/users" component={NavLink}>
So far so good. The last thing we have to make is to make sure that the ListItemButton component is going to be highlighted whenever the link in the search bar matches the NavLink specified path.
The good news is that the <ListItemButton>
has a predefined prop that makes it looks on the screen as selected, so it highlights it a bit darker. this prop is called the selected
prop, it accepts a Boolean, (true or false). SO:
How can we do this? we can do it by comparing if the URL in the searchbar matches the path we specified in the to
prop.
Now this was the introduction of why I want to find out the currently used path.
So here's the question:
as I know, the only way to do this is to compare if the currently visited link is the same path as the NavLink or Link component by doing something like so:
<ListItemButton to="/users" component={NavLink} selected={pathname === "/users"}>
I know that this question was answered before, I saw the thread where people were mentioning that the way to do so is by using the useLocation()
hook, they were saying you can do it like so: then do the comparison:
const { pathname } = useLocation()
However, this doesn't always work, for instance, if the user visited the following links, the ListItemButton component is Not going to be highlighted (selected):
You might say, okay, simply compare using .includes()
as follows:
<ListItemButton to="/users" component={NavLink} selected={pathname.includes("/users")}>
But remember, this shouldn't be if you have other routes in the sidenav such as:
because if you did the comparison using the .includes()
method, you'll end up highlighting (selecting) both NavLinks (ListItemButton).
Is there a built-in React Router Hook, or method that can get the route name correctly, the exact name that is defined by me in the <BrowserRouter>
in my index.js file?
Full example in codesandbox
I see, you are trying to style the button and not really the NavLink
component if I'm understanding your post correctly. I think you may be looking for the useMatch
hook.
Returns true if the URL for the given "to" value matches the current URL. This is useful for components that need to know "active" state, e.g.
<NavLink>
.
import { useMatch } from 'react-router-dom';
...
const isUserRoute = useMatch("/user");
...
<ListItemButton
to="/users"
component={NavLink}
selected={isUserRoute}
>
...
</ListItemButton>
To use the useMatch
hook for each ListItemButton
I suggest factoring ListItemButton
out into its own component.
Example:
const LinkButton = ({ children, to }) => {
const selected = useMatch(to);
return (
<ListItemButton
component={NavLink}
to={to}
selected={selected}
>
{children}
</ListItemButton>
);
};
Use the LinkButton
instead of ListItemButton
in the UI.
<div>
{listItems.map((item) => (
<LinkButton key={item.label} to={item.path}>
{item.label}
</LinkButton>
))}
</div>
If you prefer keeping the path matching localized to the parent component, import and use the useLocation
hook and matchPath
utility from react-router-dom
.
Example:
import { useLocation, matchPath } from 'react-router-dom';
...
const { pathname } = useLocation();
...
<div>
{listItems.map((item) => (
<ListItemButton
component={NavLink}
to={item.path}
selected={matchPath(item.path, pathname)}
>
{item.label}
</ListItemButton>
))}
</div>
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