Skip to content

Commit ab57fa8

Browse files
mattiamanzatitimdorr
authored andcommitted
feat: make Link accept function as children (#3881)
* feat: make Link accept function as children * feat: added API docs * fix: code style * fix: rollback children handling * feat: CustomLinkComponent example
1 parent 5a74d46 commit ab57fa8

File tree

5 files changed

+113
-0
lines changed

5 files changed

+113
-0
lines changed

modules/Link.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class Link extends React.Component {
1212
location: PropTypes.object,
1313
activeOnlyWhenExact: PropTypes.bool,
1414
isActive: PropTypes.func,
15+
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
1516

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

63+
handleTransition = () => {
64+
this.context.router.transitionTo(this.props.to)
65+
}
66+
6267
render() {
6368
const { router } = this.context
6469
const {
@@ -81,6 +86,18 @@ class Link extends React.Component {
8186
this.props
8287
)
8388

89+
// If children is a function, we are using a Function as Children Component
90+
// so useful values will be passed down to the children function.
91+
if (typeof rest.children == 'function') {
92+
return rest.children({
93+
isActive,
94+
location,
95+
href: router ? router.createHref(to) : to,
96+
onClick: this.handleClick,
97+
transition: this.handleTransition
98+
})
99+
}
100+
84101
// Maybe we should use <Match> here? Not sure how the custom `isActive`
85102
// prop would shake out, also, this check happens a LOT so maybe its good
86103
// to optimize here w/ a faster isActive check, so we'd need to bench mark

modules/__tests__/Link-test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,4 +282,22 @@ describe('Link', () => {
282282
expect(a.className).toEqual('active')
283283
})
284284
})
285+
286+
describe('accepts function as children', () => {
287+
it('renders the child component with isActive', () => {
288+
const div = document.createElement('div')
289+
render((
290+
<Link
291+
to='/foo'
292+
location={{ pathname: '/foo/bar' }}
293+
>
294+
{
295+
({isActive}) => <a className={isActive ? 'active' : ''}>Test!</a>
296+
}
297+
</Link>
298+
), div)
299+
const a = div.querySelector('a')
300+
expect(a.className).toEqual('active')
301+
})
302+
})
285303
})

website/api/Link.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,27 @@ Provides declarative, accessible navigation around your application.
88
</Link>
99
```
1010

11+
## `children: node | func`
12+
13+
The Link component also accepts a function as children.
14+
This will allow you to use custom component to render the link
15+
or use the router with react-native.
16+
17+
Children function parameter is an object with the following keys:
18+
19+
- `isActive`: (bool) whenever the Link is active
20+
- `location`: the location passed to the Link
21+
- `href`: (string) with the router url
22+
- `onClick`: (func) the dom onClick event handler
23+
- `transition`: (func) a shortcut to router.transitionTo with the "to" setted on the link
24+
25+
```js
26+
<Link to="/courses">{
27+
({isActive, location, href, onClick, transition}) =>
28+
<RaisedButton label="Courses" onClick={onClick} primary={isActive} href={href} />
29+
}</Link>
30+
```
31+
1132
## `to: string | object`
1233

1334
The pathname or location descriptor to link to.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// To run this example locally, create a new project with
2+
// 'create-react-app', install the router with 'npm i react-router@next',
3+
// then copy/paste the code below into `src/App.js` of your new project.
4+
// For more info on 'create-react-app', see https://github.com/facebookincubator/create-react-app
5+
6+
import React from 'react'
7+
import Match from 'react-router/Match'
8+
import Link from 'react-router/Link'
9+
import Router from 'react-router/BrowserRouter'
10+
11+
const OldSchoolMenuLink = ({onClick, href, isActive, label}) => (
12+
<div className={isActive ? 'active' : ''}>
13+
{isActive ? '> ' : ''}
14+
<a href={href} onClick={onClick}>
15+
{label}
16+
</a>
17+
</div>
18+
)
19+
20+
21+
class CustomLinkComponent extends React.Component {
22+
render() {
23+
return (
24+
<Router>
25+
<div>
26+
<Link activeOnlyWhenExact to="/">{ ({isActive, onClick, href}) => <OldSchoolMenuLink label="Home" onClick={onClick} href={href} isActive={isActive} /> }</Link>
27+
<Link to="/about">{ (params) => <OldSchoolMenuLink label="About" {...params} /> }</Link>
28+
29+
<hr/>
30+
31+
<Match exactly pattern="/" component={Home} />
32+
<Match pattern="/about" component={About} />
33+
</div>
34+
</Router>
35+
)
36+
}
37+
}
38+
39+
const Home = () => (
40+
<div>
41+
<h2>Home</h2>
42+
</div>
43+
)
44+
45+
const About = () => (
46+
<div>
47+
<h2>About</h2>
48+
</div>
49+
)
50+
51+
export default CustomLinkComponent
52+

website/routes.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ export const EXAMPLES = [
2626
load: require('bundle?lazy!./examples/Auth'),
2727
loadSource: require('bundle?lazy!!prismjs?lang=jsx!./.examples/Auth.js')
2828
},
29+
{ name: 'Custom Link Component',
30+
path: '/custom-link-component',
31+
load: require('bundle?lazy!./examples/CustomLinkComponent'),
32+
loadSource: require('bundle?lazy!!prismjs?lang=jsx!./.examples/CustomLinkComponent.js')
33+
},
2934
{ name: 'Preventing Transitions',
3035
path: '/preventing-transitions',
3136
load: require('bundle?lazy!./examples/PreventingTransitions'),

0 commit comments

Comments
 (0)