Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions modules/Link.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Link extends React.Component {
location: PropTypes.object,
activeOnlyWhenExact: PropTypes.bool,
isActive: PropTypes.func,
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),

// props we have to deal with but aren't necessarily
// part of the Link API
Expand Down Expand Up @@ -59,6 +60,10 @@ class Link extends React.Component {
}
}

handleTransition = () => {
this.context.router.transitionTo(this.props.to)
}

render() {
const { router } = this.context
const {
Expand All @@ -81,6 +86,18 @@ class Link extends React.Component {
this.props
)

// If children is a function, we are using a Function as Children Component
// so useful values will be passed down to the children function.
if (typeof rest.children == 'function') {
return rest.children({
isActive,
location,
href: router ? router.createHref(to) : to,
onClick: this.handleClick,
transition: this.handleTransition
})
}

// Maybe we should use <Match> here? Not sure how the custom `isActive`
// prop would shake out, also, this check happens a LOT so maybe its good
// to optimize here w/ a faster isActive check, so we'd need to bench mark
Expand Down
18 changes: 18 additions & 0 deletions modules/__tests__/Link-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,4 +282,22 @@ describe('Link', () => {
expect(a.className).toEqual('active')
})
})

describe('accepts function as children', () => {
it('renders the child component with isActive', () => {
const div = document.createElement('div')
render((
<Link
to='/foo'
location={{ pathname: '/foo/bar' }}
>
{
({isActive}) => <a className={isActive ? 'active' : ''}>Test!</a>
}
</Link>
), div)
const a = div.querySelector('a')
expect(a.className).toEqual('active')
})
})
})
21 changes: 21 additions & 0 deletions website/api/Link.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,27 @@ Provides declarative, accessible navigation around your application.
</Link>
```

## `children: node | func`

The Link component also accepts a function as children.
This will allow you to use custom component to render the link
or use the router with react-native.

Children function parameter is an object with the following keys:

- `isActive`: (bool) whenever the Link is active
- `location`: the location passed to the Link
- `href`: (string) with the router url
- `onClick`: (func) the dom onClick event handler
- `transition`: (func) a shortcut to router.transitionTo with the "to" setted on the link

```js
<Link to="/courses">{
({isActive, location, href, onClick, transition}) =>
<RaisedButton label="Courses" onClick={onClick} primary={isActive} href={href} />
}</Link>
```

## `to: string | object`

The pathname or location descriptor to link to.
Expand Down
52 changes: 52 additions & 0 deletions website/examples/CustomLinkComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// To run this example locally, create a new project with
// 'create-react-app', install the router with 'npm i react-router@next',
// then copy/paste the code below into `src/App.js` of your new project.
// For more info on 'create-react-app', see https://github.com/facebookincubator/create-react-app

import React from 'react'
import Match from 'react-router/Match'
import Link from 'react-router/Link'
import Router from 'react-router/BrowserRouter'

const OldSchoolMenuLink = ({onClick, href, isActive, label}) => (
<div className={isActive ? 'active' : ''}>
{isActive ? '> ' : ''}
<a href={href} onClick={onClick}>
{label}
</a>
</div>
)


class CustomLinkComponent extends React.Component {
render() {
return (
<Router>
<div>
<Link activeOnlyWhenExact to="/">{ ({isActive, onClick, href}) => <OldSchoolMenuLink label="Home" onClick={onClick} href={href} isActive={isActive} /> }</Link>
<Link to="/about">{ (params) => <OldSchoolMenuLink label="About" {...params} /> }</Link>

<hr/>

<Match exactly pattern="/" component={Home} />
<Match pattern="/about" component={About} />
</div>
</Router>
)
}
}

const Home = () => (
<div>
<h2>Home</h2>
</div>
)

const About = () => (
<div>
<h2>About</h2>
</div>
)

export default CustomLinkComponent

5 changes: 5 additions & 0 deletions website/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ export const EXAMPLES = [
load: require('bundle?lazy!./examples/Auth'),
loadSource: require('bundle?lazy!!prismjs?lang=jsx!./.examples/Auth.js')
},
{ name: 'Custom Link Component',
path: '/custom-link-component',
load: require('bundle?lazy!./examples/CustomLinkComponent'),
loadSource: require('bundle?lazy!!prismjs?lang=jsx!./.examples/CustomLinkComponent.js')
},
{ name: 'Preventing Transitions',
path: '/preventing-transitions',
load: require('bundle?lazy!./examples/PreventingTransitions'),
Expand Down