diff --git a/docs/api/README.md b/docs/api/README.md index 8dabbedf4c..0b7d003915 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -1,8 +1,9 @@ -React Router API +React Router API ================ - [`Router.run`](/docs/api/run.md) - [`Router.create`](/docs/api/create.md) +- [`Router Context`](/docs/api/RouterContext.md) - [`Location`](/docs/api/Location.md) - [`Transition`](/docs/api/Transition.md) diff --git a/docs/api/RouterContext.md b/docs/api/RouterContext.md new file mode 100644 index 0000000000..7fd562267f --- /dev/null +++ b/docs/api/RouterContext.md @@ -0,0 +1,160 @@ +API: Router Context +=================== + +The `context` feature of React is undocumented because its not +completely baked yet, however, it is commited to by the React team and +we use it with great effect in the Router. There are plans to make +reliance on context optional, but for now, this is what we use. + +You access the router inside of route handlers with +`this.context.router`. Its got a few useful methods on it. + +### `transitionTo(routeNameOrPath, [params[, query]])` + +Programmatically transition to a new route. + +#### Examples + +```js +this.context.router.transitionTo('user', {id: 10}, {showAge: true}); +this.context.router.transitionTo('about'); +this.context.router.transitionTo('/users/10?showAge=true'); +this.context.router.transitionTo('http://example.com/users/10?showAge=true'); +``` + +### `replaceWith(routeNameOrPath, [params[, query]])` + +Programmatically replace current route with a new route. Does not add an +entry into the browser history. + +#### Examples + +```js +this.context.router.replaceWith('user', {id: 10}, {showAge: true}); +this.context.router.replaceWith('about'); +this.context.router.replaceWith('/users/10?showAge=true'); +``` + +### `goBack()` + +Programmatically go back to the last route and remove the most recent +entry from the browser history. Returns `true` unless it's the first entry +in the app history. + +#### Example + +```js +this.context.router.goBack(); +``` + +If you want to make sure there was a history entry to go back to, use the return value: + +```js +if (!this.context.router.goBack()) { + this.context.router.transitionTo('/otherpage') +} +``` + +### `makePath(routeName, params, query)` + +Creates a URL path to a route. + +### `makeHref(routeName, params, query)` + +Creates an `href` to a route. + +#### Example + +```js +// given a route like this: +// +this.context.router.makeHref('user', {userId: 123}); // "users/123" +``` + +### `getPath()` + +Returns the current URL path, including query string. + +### `getPathname()` + +Returns the current URL path without the query string. + +### `getParams()` + +Returns a hash of the currently active URL params. + +### `getQuery()` + +Returns a hash of the currently active query params. + +### `isActive(routeName, params, query)` + +Returns `true` if a route, params, and query are active, `false` +otherwise. + +### `getRoutes()` + +Returns an array of the currently active routes, in nesting order. + +Examples +-------- + +Often you'll want access to params and query: + +```js +// route + + +// handler +var User = React.createClass({ + render: function () { + var name = this.context.router.getParams().name; + return ( +
+

{name}

+
+ ); + } +}); +``` + +Let's say you are using bootstrap and want to get `active` on those `li` +tags for the Tabs: + +```js +var Link = require('react-router').Link; + +var Tab = React.createClass({ + render: function () { + var { router } = this.context; + var isActive = router.isActive(this.props.to, this.props.params, this.props.query); + var className = isActive ? 'active' : ''; + var link = ( + + ); + return
  • {link}
  • ; + } + +}); + +// use it just like , and you'll get an anchor wrapped in an `li` +// with an automatic `active` class on both. +Foo +``` + +Some navigation: + +```js +React.createClass({ + render: function() { + var { router } = this.context; + return ( +
    router.transitionTo('foo')}>Go to foo
    +
    router.replaceWith('bar')}>Go to bar without creating a new history entry
    +
    router.goBack()}>Go back
    + ); + } +}); +``` + + diff --git a/docs/api/components/RouteHandler.md b/docs/api/components/RouteHandler.md index 6f3d858ff8..9b79a0912b 100644 --- a/docs/api/components/RouteHandler.md +++ b/docs/api/components/RouteHandler.md @@ -4,6 +4,14 @@ API: `RouteHandler` (component) A `` renders the handler of the route at the level of the route hierarchy in which it is used. +Router Context +-------------- + +Router handlers are rendered with a router in their context with useful +methods. + +Please see [`Router Context`](/docs/api/RouterContext.md) + Static Lifecycle Methods ------------------------ @@ -53,3 +61,4 @@ var Settings = React.createClass({ //... }); ``` + diff --git a/docs/api/mixins/Navigation.md b/docs/api/mixins/Navigation.md index 3377ecd74d..3449f73e79 100644 --- a/docs/api/mixins/Navigation.md +++ b/docs/api/mixins/Navigation.md @@ -1,90 +1,5 @@ API: `Navigation` (mixin) ========================== -A mixin for components that need to create URLs and/or initiate -transitions to other routes. +Deprecated, please see [Router Context](/docs/api/RouterContext.md) -Instance Methods ----------------- - -### `transitionTo(routeNameOrPath, [params[, query]])` - -Programmatically transition to a new route. - -#### Examples - -```js -this.transitionTo('user', {id: 10}, {showAge: true}); -this.transitionTo('about'); -this.transitionTo('/users/10?showAge=true'); -this.transitionTo('http://example.com/users/10?showAge=true'); -``` - -### `replaceWith(routeNameOrPath, [params[, query]])` - -Programmatically replace current route with a new route. Does not add an -entry into the browser history. - -#### Examples - -```js -this.replaceWith('user', {id: 10}, {showAge: true}); -this.replaceWith('about'); -this.replaceWith('/users/10?showAge=true'); -``` - -### `goBack()` - -Programmatically go back to the last route and remove the most recent -entry from the browser history. Returns `true` unless it's the first entry -in the app history. - -#### Example - -```js -this.goBack(); -``` - -If you want to make sure there was a history entry to go back to, use the return value: - -```js -if (!this.goBack()) { - this.transitionTo('/otherpage') -} -``` - -### `makePath(routeName, params, query)` - -Creates a URL path to a route. - -### `makeHref(routeName, params, query)` - -Creates an `href` to a route. Use this along with `State` when you -need to build components similar to `Link`. - -#### Example - -```js -// given a route like this: -// -this.makeHref('user', {userId: 123}); // "users/123" -``` - -Example -------- - -```js -var Navigation = require('react-router').Navigation; - -React.createClass({ - mixins: [Navigation], - - render: function() { - return ( -
    this.transitionTo('foo')}>Go to foo
    -
    this.replaceWith('bar')}>Go to bar without creating a new history entry
    -
    this.goBack()}>Go back
    - ); - } -}); -``` diff --git a/docs/api/mixins/State.md b/docs/api/mixins/State.md index 90fbb82957..9313d2df87 100644 --- a/docs/api/mixins/State.md +++ b/docs/api/mixins/State.md @@ -1,85 +1,6 @@ API: `State` (mixin) ========================== -A mixin for components that need to know about the active params, query -and routes. Any handler on a route with dynamic segments will want to -use this. +Deprecated, please see [Router Context](/docs/api/RouterContext.md) -Instance Methods ----------------- -### `getPath()` - -Returns the current URL path, including query string. - -### `getPathname()` - -Returns the current URL path without the query string. - -### `getParams()` - -Returns a hash of the currently active URL params. - -### `getQuery()` - -Returns a hash of the currently active query params. - -### `isActive(routeName, params, query)` - -Returns `true` if a route, params, and query are active, `false` -otherwise. - -### `getRoutes()` - -Returns an array of the currently active routes, in nesting order. - -Examples --------- - -Usually you'll just want access to params and query: - -```js -// route - - -// handler -var User = React.createClass({ - mixins: [ Router.State ], - - render: function () { - var name = this.getParams().name; - return ( -
    -

    {name}

    -
    - ); - } -}); -``` - -Let's say you are using bootstrap and want to get `active` on those `li` -tags for the Tabs: - -```js -var Link = require('react-router').Link; -var State = require('react-router').State; - -var Tab = React.createClass({ - - mixins: [ State ], - - render: function () { - var isActive = this.isActive(this.props.to, this.props.params, this.props.query); - var className = isActive ? 'active' : ''; - var link = ( - - ); - return
  • {link}
  • ; - } - -}); - -// use it just like , and you'll get an anchor wrapped in an `li` -// with an automatic `active` class on both. -Foo -``` diff --git a/docs/guides/flux.md b/docs/guides/flux.md index ea290a7317..60950aa84f 100644 --- a/docs/guides/flux.md +++ b/docs/guides/flux.md @@ -13,21 +13,19 @@ creating a cycle indirectly. To avoid this, you can do one of three things: -1. Send the component to the action, think of it like `event.target` in - DOM events if it bothers you. +1. Send the router context to the action: ```js var SomeActionCreators = require('./SomeActionCreators'); var Something = React.createClass({ - mixins: [ Router.Navigation ], handleClick () { - SomeActionCreators.doStuff({ sourceComponent: this }); + SomeActionCreators.doStuff({ router: this.context.router }); } }); ``` and then in `SomeActionCreators.doStuff` call - `payload.sourceComponent.transitionTo(...)` + `payload.router.transitionTo(...)` 2. Use some sort of application container, or module container for the router instance. @@ -112,9 +110,8 @@ To avoid this, you can do one of three things: } ``` - Accessing route and params from action creators ---------------------------------- +----------------------------------------------- You can create your own `RouterStore` and fire an action in `run` callback: diff --git a/docs/guides/overview.md b/docs/guides/overview.md index d2c23e9dcb..222d51a664 100644 --- a/docs/guides/overview.md +++ b/docs/guides/overview.md @@ -285,8 +285,8 @@ Dynamic Segments When we added the `message` route, we introduced a "dynamic segment" to the URL. These segments get parsed from the url and are available in -the `run` callback, or from the `State` mixin. Let's see how we can -access the params. +the `run` callback, or from `this.context.router` in a route handler. +Let's see how we can access the params. Remember our message route looks like this: @@ -298,16 +298,15 @@ Lets look at accessing the `messageId` in `Message`. ```js var Message = React.createClass({ - mixins: [Router.State], render: function () { return ( -
    {this.getParams().messageId}
    +
    {this.context.router.getParams().messageId}
    ); } }); ``` -Assuming the user navigates to `/inbox/123`, `this.getParams().messageId` is +Assuming the user navigates to `/inbox/123`, `this.context.router.getParams().messageId` is going to be `'123'`. Alternatively, you can pass the param data down through the view @@ -361,13 +360,11 @@ If you would rather force route handlers to re-mount when transitioning between ```js var App = React.createClass({ - - mixins: [Router.State], - getHandlerKey: function () { var childDepth = 1; // assuming App is top-level route - var key = this.getRoutes()[childDepth].name; - var id = this.getParams().id; + var { router } = this.context; + var key = router.getRoutes()[childDepth].name; + var id = router.getParams().id; if (id) { key += id; } return key; }, @@ -447,12 +444,6 @@ That's the gist of what this router is all about, but there's a lot more it has to offer. Check out the [API Docs][API] to learn about redirecting transitions, query parameters and more. - [AsyncState]:../api/mixins/AsyncState.md - [Route]:../api/components/Route.md - [create]: ../api/create.md - [API]:../api/ - [path-matching]:./path-matching.md - CommonJS Guide -------------- diff --git a/docs/guides/testing.md b/docs/guides/testing.md index b86096c3cd..83f24e7ce0 100644 --- a/docs/guides/testing.md +++ b/docs/guides/testing.md @@ -3,7 +3,7 @@ React Router Testing Because the router relies heavily on the lesser known `context` feature of React, it can be a pain in the neck to test your components that have -things like `` or mixin `State` and `Navigation`. +things like `` or rely on `this.context.router`. You simply have to stub out the context you need. @@ -15,17 +15,7 @@ React.render(, testElement); You'll get something like: ``` -"Warning: Required context `makePath` was not specified in `Link`. Check the render method of `IndividualComponent`." -"Warning: Required context `makeHref` was not specified in `Link`. Check the render method of `IndividualComponent`." -"Warning: Required context `transitionTo` was not specified in `Link`. Check the render method of `IndividualComponent`." -"Warning: Required context `replaceWith` was not specified in `Link`. Check the render method of `IndividualComponent`." -"Warning: Required context `goBack` was not specified in `Link`. Check the render method of `IndividualComponent`." -"Warning: Required context `getCurrentPath` was not specified in `Link`. Check the render method of `IndividualComponent`." -"Warning: Required context `getCurrentRoutes` was not specified in `Link`. Check the render method of `IndividualComponent`." -"Warning: Required context `getCurrentPathname` was not specified in `Link`. Check the render method of `IndividualComponent`." -"Warning: Required context `getCurrentParams` was not specified in `Link`. Check the render method of `IndividualComponent`." -"Warning: Required context `getCurrentQuery` was not specified in `Link`. Check the render method of `IndividualComponent`." -"Warning: Required context `isActive` was not specified in `Link`. Check the render method of `IndividualComponent`." +"Warning: Required context `router` was not specified in `Link`. Check the render method of `IndividualComponent`." ``` So we can just wrap up the thing we want to test in a different @@ -33,36 +23,18 @@ component and stub out the `context` stuff. ```js // wrap it up first: -var { func } = React.PropTypes; - var TestWrapper = React.createClass({ childContextTypes: { - makePath: func, - makeHref: func, - transitionTo: func, - replaceWith: func, - goBack: func, - getCurrentPath: func, - getCurrentRoutes: func, - getCurrentPathname: func, - getCurrentParams: func, - getCurrentQuery: func, - isActive: func, + router: React.PropTypes.object }, getChildContext () { - return { - makePath () {}, - makeHref () {}, - transitionTo () {}, - replaceWith () {}, - goBack () {}, - getCurrentPath () {}, - getCurrentRoutes () {}, - getCurrentPathname () {}, - getCurrentParams () {}, - getCurrentQuery () {}, - isActive () {}, + return router: { + makePath () {}, + makeHref () {}, + isActive () {}, + // and whichever router methods your component uses + } }; }, @@ -88,33 +60,25 @@ Copy/paste this helper into your test utils to make things a bit easier: var stubRouterContext = (Component, props, stubs) => { return React.createClass({ childContextTypes: { - makePath: func, - makeHref: func, - transitionTo: func, - replaceWith: func, - goBack: func, - getCurrentPath: func, - getCurrentRoutes: func, - getCurrentPathname: func, - getCurrentParams: func, - getCurrentQuery: func, - isActive: func, + router: object }, getChildContext () { - return Object.assign({ - makePath () {}, - makeHref () {}, - transitionTo () {}, - replaceWith () {}, - goBack () {}, - getCurrentPath () {}, - getCurrentRoutes () {}, - getCurrentPathname () {}, - getCurrentParams () {}, - getCurrentQuery () {}, - isActive () {}, - }, stubs); + return { + router: Object.assign({ + makePath () {}, + makeHref () {}, + transitionTo () {}, + replaceWith () {}, + goBack () {}, + getCurrentPath () {}, + getCurrentRoutes () {}, + getCurrentPathname () {}, + getCurrentParams () {}, + getCurrentQuery () {}, + isActive () {}, + }, stubs) + }; }, render () {