Router: Having access to isCurrent for active <Link />, on other element besides <Link>

Created on 29 Aug 2019  路  3Comments  路  Source: reach/router

This "issue" is just a question, I am not sure if I am doing it right, or if there is a better way. And what if the other elements are not the children?

My code so far is the following:

<S.NavLink key={route.name} to={route.path}>
    <S.ListItem color={color} inverted={invertColor(color, true)}>
        <Icon icon={route.icon} size={27}></Icon>
        <span>{route.name}</span>
    </S.ListItem>
</S.NavLink>

And simply reusing the code from the docs, wasn't enough, because I have styles in ListItem, and Icon that were overriding the color/bgColor attributes, so I needed ListItem to be aware of isCurrent prop.

What I ended up doing was (changes applied on the example code):

const NavLink = props => {
    const [current, setCurrent] = useState(false)

    return (
        <Link
            {...props}
            getProps={({ isCurrent }) => {
                setCurrent(isCurrent);

                return {
                    style: {
                        color: isCurrent ? 'red' : 'blue',
                    },
                }
            }}
        > 
            // Magic is here ( I am just cloning the children, and passing the prop manually)

            {React.Children.map(props.children, child =>
                React.cloneElement(child, { isCurrent: current })
            )}
        </Link>
    )
}

Is this ok? And how can I extend the isCurrent prop, to an element that has no relation with the <Link />

Most helpful comment

Yep, use match.

function ListItemLink({ to, children }) {
  return (
    <Match path={to}>
      {({ match }) => (
        <li className={match ? "active" : ""}>
          <Link to={to}>{children}</Link>
        </li>
      )}
    </Match>
  );
}

<ul>
  <ListItemLink to="/somewhere">Hey hey!</ListItemLink>
</ul>

All 3 comments

I think it's assumed that we write a <Match /> for each such case.

Yep, use match.

function ListItemLink({ to, children }) {
  return (
    <Match path={to}>
      {({ match }) => (
        <li className={match ? "active" : ""}>
          <Link to={to}>{children}</Link>
        </li>
      )}
    </Match>
  );
}

<ul>
  <ListItemLink to="/somewhere">Hey hey!</ListItemLink>
</ul>

Rather than have an if for each individual Menu child, filter your elements into as many groups as you need to implement (with "current" this would be 2, uncurrent and current :) ), and render the two groups separately but in batch. I don't think I would want a dozen ifs for a dozen menu items.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

magnusarinell picture magnusarinell  路  3Comments

artoodeeto picture artoodeeto  路  4Comments

micabe picture micabe  路  4Comments

sseppola picture sseppola  路  4Comments

ryanflorence picture ryanflorence  路  4Comments