Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support to get the component instance that withRouter HOC wraps #3735

Merged
merged 8 commits into from
Aug 19, 2016
23 changes: 21 additions & 2 deletions modules/__tests__/withRouter-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ describe('withRouter', function () {
propTypes: {
router: routerShape.isRequired
}
testFunction() {
return 'hello from the test function'
}
render() {
expect(this.props.router).toExist()
return <h1>App</h1>
Expand All @@ -28,7 +31,7 @@ describe('withRouter', function () {
})

it('puts router on context', function (done) {
const WrappedApp = withRouter(App)
const WrappedApp = withRouter()(App)

render((
<Router history={createHistory('/')}>
Expand All @@ -40,7 +43,7 @@ describe('withRouter', function () {
})

it('still uses router prop if provided', function (done) {
const Test = withRouter(function (props) {
const Test = withRouter()(function (props) {
props.test(props)
return null
})
Expand All @@ -59,4 +62,20 @@ describe('withRouter', function () {

render(<Test router={router} test={test} />, node, done)
})

it('should support withRefs as a parameter',function (done) {
const WrappedApp = withRouter({ withRef:true })(App)
const router = {
push() {},
replace() {},
go() {},
goBack() {},
goForward() {},
setRouteLeaveHook() {},
isActive() {}
}
const component = render((<WrappedApp router={router}/>), node, done)
expect(component.getWrappedInstance().testFunction()).toEqual('hello from the test function')
})

})
43 changes: 30 additions & 13 deletions modules/withRouter.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,40 @@
import React from 'react'
import hoistStatics from 'hoist-non-react-statics'
import { routerShape } from './PropTypes'
import warning from './routerWarning'

function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
export default function withRouter(options) {
const wrapWithRouter = function (WrappedComponent) {
const WithRouter = React.createClass({
contextTypes: { router: routerShape },
propTypes: { router: routerShape },
getWrappedInstance() {
warning(options && options.withRef, 'To access the wrappedInstance you must provide {withRef : true} as the first argument of the withRouter call')
return this.refs.wrappedInstance
},
render() {
const router = this.props.router || this.context.router
if(options && options.withRef) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please match the code style elsewhere in the project

return <WrappedComponent {...this.props} ref="wrappedInstance" router={router} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a function ref

}else{
return <WrappedComponent {...this.props} router={router} />
}
}
})
WithRouter.displayName = `withRouter(${getDisplayName(WrappedComponent)})`
WithRouter.WrappedComponent = WrappedComponent

export default function withRouter(WrappedComponent) {
const WithRouter = React.createClass({
contextTypes: { router: routerShape },
propTypes: { router: routerShape },
render() {
const router = this.props.router || this.context.router
return <WrappedComponent {...this.props} router={router} />
}
})
return hoistStatics(WithRouter, WrappedComponent)
}

WithRouter.displayName = `withRouter(${getDisplayName(WrappedComponent)})`
WithRouter.WrappedComponent = WrappedComponent

return hoistStatics(WithRouter, WrappedComponent)
if(typeof(options) === 'function') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? Just make options be the second arg. This is the less common use case and it's not worth the deprecation or making ergonomics worse for the common case.

warning(false,'passing a component to the first invocation of withRouter has been depreciated, withRouter'+
'now needs to be invoked twice, once to pass the options through, and a second time with the component eg. withRouter()(MyComponent) or withRouter({withRef:true})(MyComponent)')
return wrapWithRouter(options)
}else{
return wrapWithRouter
}
}