React-router: Feature request: `render` prop on `Link`

Created on 19 Feb 2017  路  9Comments  路  Source: ReactTraining/react-router

I feel the Link and NavLink components in react-router-dom are lacking flexibility when it comes to custom links.

Take the following html example:

<li className='active'>
  <a href='/'>Home</a>
</li>

The v4 documentation is clear on how to handle custom links, however it could be handled more easily with a render prop in the Link component and having NavLink forward render and isActive.

<NavLink exact to='/' render={({isActive, href, onClick}) => (
   <li className={isActive ? 'active' : ''}>
     <a href={href} onClick={onClick}>Home</a>
  </li>
)} />

I am happy to submit a PR if you think this is a good idea.

Most helpful comment

btw. something similar (children as function) was available in the alpha versions: https://github.com/ReactTraining/react-router/pull/3881
would love to get it back.

All 9 comments

btw. something similar (children as function) was available in the alpha versions: https://github.com/ReactTraining/react-router/pull/3881
would love to get it back.

You can do exactly that as long as you swap out some words for other words:

<Route exact to path='/' children={({match, href, onClick}) => (
   <li className={match ? 'active' : ''}>
     <NavLink to={href} onClick={onClick}>Home</NavLink>
  </li>
)} />

It's all there already :smile:

@timdorr thanks. This makes sense but I see some issues with this.

As far as I can see, onClick is not a property that is received by the handler for the children prop on the Route component, nor is href for that matter (but that can be sourced elsewhere).

But the point of this issue is really to allow Link to work with third-party components such as Button, MenuItem and NavItem from React Router Bootstrap or React MDL.

I appreciate that Route can take care of most of the work when it comes to active or not, and it is a really nice pattern. Everything in your comment above looks great, except that onClick is undefined.

The handleClick method on the Link component comes with some extra functionality, so instead of changing the render of Link, perhaps it may be an idea to move the handleClick method up into the router context?

Oh yeah, sorry about that. But you can just move it up the chain and wrap all of that in a functional component that takes those props. 4.0 is all about composability!

Can you make an example how that would look like with a NavLink from reactstrap or similar? I just cannot get my head around what you wrote. The Route render method does not provide an onClick method, so your example does not work.

With the alpha, it was quite easy to render a different component than the .

<Link to={'/admin'}>{({ href, onClick }) => <NavLink onClick={onClick} href={href}>Admin</NavLink>}</Link>

The result was just one tag with a perfectly setup onClick handler.

Can you please tell us how that example would look like with 4.0.0?

@p0wl I created my own LinkContainer, I use it like so:

<LinkContainer exact to='/about'>
   <NavLink>About</NavLink>
</LinkContainer>

Perhaps there's a better way, I don't know, but it works.

AS @marcgreenstock has mentioned, without such flexibility it is really painful to work with third party components that have already got a "" in it, e.g. Grommet's Anchor. It does not make sense to have Link in Anchor, nor Anchor in Link, since either way we will have "" inside a "".

It would be better if we do not have to use to trigger a route change, but I could not find that API in the documentation.

I too would prefer a render prop for this functionality. It seems silly for us to have to compute whether or not the link is active on our own when NavLink already knows this.

Here's a simple example of the 'workaround' that I used, it's pretty unfortunate to need this:

interface IProps extends RouteComponentProps<any> {
  pathToLink: string //the path to link to, i.e.: `/about/team`
  linkText: string
}

const WrappedNavLink = ({pathname, ...routeComponentProps}: IProps) => {
  const isActiveLink = routeComponentProps.location.pathname === pathToLink
  return (
    <NavLink
      to={{pathname: pathToLink}}
    >
      <Text color={isActiveLink ? 'black' : 'grey'}>{linkText}</Text>
    </NavLink>
  )
}

export default withRouter(WrappedNavLink)
Was this page helpful?
0 / 5 - 0 ratings

Related issues

imWildCat picture imWildCat  路  3Comments

stnwk picture stnwk  路  3Comments

maier-stefan picture maier-stefan  路  3Comments

yormi picture yormi  路  3Comments

winkler1 picture winkler1  路  3Comments