react-router icon indicating copy to clipboard operation
react-router copied to clipboard

[v6] [Feature]: bring back isActive prop in NavLink component

Open smashercosmo opened this issue 2 years ago • 9 comments

What is the new or updated feature that you are suggesting?

NavLink's isActive prop has been removed in v6 either intentionally or accidentally. I suggest to bring it back.

Why should this feature be included?

isActive prop helps with use cases like when you need to highlight a link or a tab depending on url's query parameters

smashercosmo avatar Sep 01 '21 21:09 smashercosmo

Thanks for bringing this to our attention, @smashercosmo. We should add this in v6, @chaance.

mjackson avatar Sep 01 '21 23:09 mjackson

Actually, I think I may have spoken too soon. Just thinking about this some more...

The reason we needed a <NavLink isActive> prop in v5 is because it was originally released in a world without React hooks, so you couldn't easily get access to the router's state. However, in v6 we have a whole bunch of hooks that should make it really easy to do whatever you need to do @smashercosmo.

For example, let's say you're building a product filter UI where you keep the value of the filter in the query string. And let's say you're selling shoes and you want to filter by brand. Your URLs might look something like /shoes?brand=nike or /shoes?brand=puma, and you could <Link> to them.

Our <NavLink> API is designed for navigation links, which tend to update the URL pathname. But in this case, the links to these URLs feel more like filters and less like navigation since they update the query string instead of the pathname. And since <NavLink> is not able to match the search string as well, it doesn't really work for this use case.

However, v6 does give you the tools to build your own BrandLink. In this case, you could use the useSearchParams hook to access the current URLSearchParams and do your own comparison to figure out if your link should be active or not. It could be something as simple as this:

import { useSearchParams, Link } from "react-router-dom";

function BrandLink({ brand, children }) {
  let [searchParams] = useSearchParams();
  // Use your own logic here to tell if the link is active or not...
  let isActive = searchParams.get('brand') === brand;
  return (
    <Link
      className={isActive ? 'active' : ''}
      to={`/shoes?brand=${brand}`}
    >
      {children}
    </Link>
  );
}

// Then, use it like:
<BrandLink brand="nike">Nike</BrandLink>
<BrandLink brand="puma">Puma</BrandLink>

Does that make sense?

In general we are trying to steer people more towards using the tools we give them to solve their own problems instead of trying to create abstractions for solving every use case. For this specific case, I think you should already have the tools you need to get it done.

mjackson avatar Sep 09 '21 19:09 mjackson

I see your point. But how about just passing 'match' object to style and className functions along with isActive flag?

smashercosmo avatar Sep 10 '21 11:09 smashercosmo

I think the docs may require updating: NavLink

A <NavLink> is a special kind of <Link> that knows whether or not it is "active".

sebruiz avatar Dec 09 '21 15:12 sebruiz

From what I can see in 6.0.2 at least, isActive is available in both the className and style props if you provide a function.

<NavLink to="about" className={({ isActive }) => isActive ? : 'active' : 'inactive' }>
  About
</NavLink>

Not 100% sure if it covers your usecase, but seems like it?

is-jonreeves avatar Jan 21 '22 17:01 is-jonreeves

It would be really great if we could abstract the isActive logic into its own hook and then expose it so that it can be used in other components.

nicksrandall avatar Mar 16 '22 15:03 nicksrandall

How would I access the IsActive state inside a child component/element of the navlink? IOW: Say I want to change the classname of an icon inside of my NavLink based on its active/inactive state.

GiorgioG avatar Jun 04 '22 14:06 GiorgioG

I think you should stop making API changes on small versions. This is a pain to keep track of, and looks like it is for those updating docs as well. For example, You can also pass a function as children to customize the content of the <NavLink> component based on their active state, specially useful to change styles on internal elements. (gathered from here). This no longer works, only in style and classNames

alekcastillo avatar Jul 25 '22 17:07 alekcastillo

This means that this code snippet doesn't work (gathered from the same page)

<li>
          <NavLink to="tasks">
            {({ isActive }) => (
              <span
                className={
                  isActive ? activeClassName : undefined
                }
              >
                Tasks
              </span>
            )}
          </NavLink>
</li>

Passing it on the children function doesn't work either (children={({ isActive }) => (). For those reading, this is the easiest approach atm:

const location = useLocation();
const isActive = location.pathname === item.To;

alekcastillo avatar Jul 25 '22 18:07 alekcastillo