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

Add withRouter HoC #3352

Merged
merged 5 commits into from
Apr 21, 2016
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
59 changes: 4 additions & 55 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- [`<Router>`](#router)
- [`<Link>`](#link)
- [`<IndexLink>`](#indexlink)
- [`withRouter`](#withRouter-component)
- [`<RouterContext>`](#routercontext)
- [`context.router`](#contextrouter)
- `<RoutingContext>` (deprecated, use `<RouterContext>`)
Expand Down Expand Up @@ -162,6 +163,9 @@ Given a route like `<Route path="/users/:userId" />`:
### `<IndexLink>`
An `<IndexLink>` is like a [`<Link>`](#link), except it is only active when the current route is exactly the linked route. It is equivalent to `<Link>` with the `onlyActiveOnIndex` prop set.

### `withRouter(component)`
A HoC (higher-order component) that wraps another component to provide `this.props.router`. Pass in your component and it will return the wrapped component.
Copy link
Contributor

Choose a reason for hiding this comment

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

Mention that it can also be used as a decorator on classes

Copy link
Contributor

Choose a reason for hiding this comment

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

Oops, that should have been a question – I mean, "do we want to mention that?"

Copy link
Member Author

Choose a reason for hiding this comment

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

I was thinking about that, but decorators aren't in babel 6 unless you include the non-default babel-plugin-transform-decorators-legacy. Given the state of flux surrounding them, I'm hesitant to bring attention to them right now.


### `<RouterContext>`
A `<RouterContext>` renders the component tree for a given router state. Its used by `<Router>` but also useful for server rendering and integrating in brownfield development.

Expand All @@ -171,61 +175,6 @@ It also provides a `router` object on [context](https://facebook.github.io/react

Contains data and methods relevant to routing. Most useful for imperatively transitioning around the application.

To use it, you must signal to React that you need it by declaring your use of it in your component via `contextTypes`:

```js
var MyComponent = React.createClass({
contextTypes: {
router: routerShape.isRequired
},

render: function() {
// Here, you can use this.context.router.
}
})
```

To use `context.router` on a component declared as an ES2015 class, define `contextTypes` as a static property of the class:

```js
class MyComponent extends React.Component {
render() {
// Here, you can use this.context.router.
}
}

MyComponent.contextTypes = {
router: routerShape.isRequired
}
```

If you are using the class properties proposal, you can instead write:

```js
class MyComponent extends React.Component {
static contextTypes = {
router: routerShape.isRequired
}

render() {
// Here, you can use this.context.router.
}
}
```

To use `context.router` with
[stateless function components](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions), declare `contextTypes` as a static property of the component function:

```js
function MyComponent(props, context) {
// Here, you can use context.router.
}

MyComponent.contextTypes = {
router: routerShape.isRequired
}
```

##### `push(pathOrLoc)`
Transitions to a new URL, adding a new entry in the browser history.

Expand Down
13 changes: 7 additions & 6 deletions docs/Troubleshooting.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
# Troubleshooting

### `this.context.router` is `undefined`
### How do I add `this.props.router` to my component?

You need to add `router` to your component's `contextTypes` to make the router object available to you.
You need to wrap your component using `withRouter` to make the router object available to you.

```js
contextTypes: {
router: routerShape.isRequired
}
const Component = withRouter(
React.createClass({
//...
})
)
```


### Getting the previous location

```js
Expand Down
30 changes: 14 additions & 16 deletions docs/guides/ConfirmingNavigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,22 @@
You can prevent a transition from happening or prompt the user before leaving a [route](/docs/Glossary.md#route) with a leave hook.

```js
const Home = React.createClass({
const Home = withRouter(
React.createClass({

contextTypes: {
router: routerShape.isRequired
},
componentDidMount() {
this.props.router.setRouteLeaveHook(this.props.route, this.routerWillLeave)
},

componentDidMount() {
this.context.router.setRouteLeaveHook(this.props.route, this.routerWillLeave)
},
routerWillLeave(nextLocation) {
// return false to prevent a transition w/o prompting the user,
// or return a string to allow the user to decide:
if (!this.state.isSaved)
return 'Your work is not saved! Are you sure you want to leave?'
},

routerWillLeave(nextLocation) {
// return false to prevent a transition w/o prompting the user,
// or return a string to allow the user to decide:
if (!this.state.isSaved)
return 'Your work is not saved! Are you sure you want to leave?'
},
// ...

// ...

})
})
)
```
2 changes: 1 addition & 1 deletion docs/guides/NavigatingOutsideOfComponents.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Navigating Outside of Components

While you can use `this.context.router` to navigate around, many apps want to be able to navigate outside of their components. They can do that with the history the app gives to `Router`.
While you can use `this.props.router` from `withRouter` to navigate around, many apps want to be able to navigate outside of their components. They can do that with the history the app gives to `Router`.

```js
// your main file that renders a Router
Expand Down
67 changes: 33 additions & 34 deletions examples/auth-flow-async-with-query-params/app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { createClass } from 'react'
import { render } from 'react-dom'
import {
Router, Route, IndexRoute, browserHistory, Link, routerShape
Router, Route, IndexRoute, browserHistory, Link, withRouter
} from 'react-router'

function App(props) {
Expand All @@ -12,43 +12,42 @@ function App(props) {
)
}

const Form = createClass({
contextTypes: {
router: routerShape.isRequired
},
const Form = withRouter(
createClass({

getInitialState() {
return {
value: ''
}
},

submitAction(event) {
event.preventDefault()
this.context.router.push({
pathname: '/page',
query: {
qsparam: this.state.value
getInitialState() {
return {
value: ''
}
})
},
},

handleChange(event) {
this.setState({ value: event.target.value })
},
submitAction(event) {
event.preventDefault()
this.props.router.push({
pathname: '/page',
query: {
qsparam: this.state.value
}
})
},

render() {
return (
<form onSubmit={this.submitAction}>
<p>Token is <em>pancakes</em></p>
<input type="text" value={this.state.value} onChange={this.handleChange} />
<button type="submit">Submit the thing</button>
<p><Link to="/page?qsparam=pancakes">Or authenticate via URL</Link></p>
<p><Link to="/page?qsparam=bacon">Or try failing to authenticate via URL</Link></p>
</form>
)
}
})
handleChange(event) {
this.setState({ value: event.target.value })
},

render() {
return (
<form onSubmit={this.submitAction}>
<p>Token is <em>pancakes</em></p>
<input type="text" value={this.state.value} onChange={this.handleChange} />
<button type="submit">Submit the thing</button>
<p><Link to="/page?qsparam=pancakes">Or authenticate via URL</Link></p>
<p><Link to="/page?qsparam=bacon">Or try failing to authenticate via URL</Link></p>
</form>
)
}
})
)

function Page() {
return <h1>Hey, I see you are authenticated. Welcome!</h1>
Expand Down
86 changes: 42 additions & 44 deletions examples/auth-flow/app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import { render } from 'react-dom'
import { browserHistory, Router, Route, Link, routerShape } from 'react-router'
import { browserHistory, Router, Route, Link, withRouter } from 'react-router'
import auth from './auth'

const App = React.createClass({
Expand Down Expand Up @@ -55,51 +55,49 @@ const Dashboard = React.createClass({
}
})

const Login = React.createClass({
const Login = withRouter(
React.createClass({

contextTypes: {
router: routerShape.isRequired
},

getInitialState() {
return {
error: false
}
},

handleSubmit(event) {
event.preventDefault()

const email = this.refs.email.value
const pass = this.refs.pass.value

auth.login(email, pass, (loggedIn) => {
if (!loggedIn)
return this.setState({ error: true })

const { location } = this.props

if (location.state && location.state.nextPathname) {
this.context.router.replace(location.state.nextPathname)
} else {
this.context.router.replace('/')
getInitialState() {
return {
error: false
}
})
},

render() {
return (
<form onSubmit={this.handleSubmit}>
<label><input ref="email" placeholder="email" defaultValue="joe@example.com" /></label>
<label><input ref="pass" placeholder="password" /></label> (hint: password1)<br />
<button type="submit">login</button>
{this.state.error && (
<p>Bad login information</p>
)}
</form>
)
}
})
},

handleSubmit(event) {
event.preventDefault()

const email = this.refs.email.value
const pass = this.refs.pass.value

auth.login(email, pass, (loggedIn) => {
if (!loggedIn)
return this.setState({ error: true })

const { location } = this.props

if (location.state && location.state.nextPathname) {
this.props.router.replace(location.state.nextPathname)
} else {
this.props.router.replace('/')
}
})
},

render() {
return (
<form onSubmit={this.handleSubmit}>
<label><input ref="email" placeholder="email" defaultValue="joe@example.com" /></label>
<label><input ref="pass" placeholder="password" /></label> (hint: password1)<br />
<button type="submit">login</button>
{this.state.error && (
<p>Bad login information</p>
)}
</form>
)
}
})
)

const About = React.createClass({
render() {
Expand Down
12 changes: 4 additions & 8 deletions examples/auth-with-shared-root/components/Login.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import React from 'react'
import { withRouter } from 'react-router'
import auth from '../utils/auth.js'

const Login = React.createClass({

contextTypes: {
router: React.PropTypes.object
},

getInitialState() {
return {
error: false
Expand All @@ -26,9 +22,9 @@ const Login = React.createClass({
const { location } = this.props

if (location.state && location.state.nextPathname) {
this.context.router.replace(location.state.nextPathname)
this.props.router.replace(location.state.nextPathname)
} else {
this.context.router.replace('/')
this.props.router.replace('/')
}
})
},
Expand All @@ -48,4 +44,4 @@ const Login = React.createClass({

})

export default Login
export default withRouter(Login)
Loading