diff --git a/modules/Navigation.js b/modules/Navigation.js index 2086a18480..2103f3eb1f 100644 --- a/modules/Navigation.js +++ b/modules/Navigation.js @@ -1,5 +1,17 @@ +var warning = require('react/lib/warning'); var PropTypes = require('./PropTypes'); +function deprecatedMethod(routerMethodName, fn) { + return function () { + warning( + false, + `Router.Navigation is deprecated. Please use this.context.router.${routerMethodName}() instead` + ); + + return fn.apply(this, arguments); + }; +} + /** * A mixin for components that modify the URL. * @@ -7,11 +19,11 @@ var PropTypes = require('./PropTypes'); * * var MyLink = React.createClass({ * mixins: [ Router.Navigation ], - * handleClick: function (event) { + * handleClick(event) { * event.preventDefault(); * this.transitionTo('aRoute', { the: 'params' }, { the: 'query' }); * }, - * render: function () { + * render() { * return ( * Click me! * ); @@ -21,51 +33,47 @@ var PropTypes = require('./PropTypes'); var Navigation = { contextTypes: { - makePath: PropTypes.func.isRequired, - makeHref: PropTypes.func.isRequired, - transitionTo: PropTypes.func.isRequired, - replaceWith: PropTypes.func.isRequired, - goBack: PropTypes.func.isRequired + router: PropTypes.router.isRequired }, /** * Returns an absolute URL path created from the given route * name, URL parameters, and query values. */ - makePath: function (to, params, query) { - return this.context.makePath(to, params, query); - }, + makePath: deprecatedMethod('makePath', function (to, params, query) { + return this.context.router.makePath(to, params, query); + }), /** * Returns a string that may safely be used as the href of a * link to the route with the given name. */ - makeHref: function (to, params, query) { - return this.context.makeHref(to, params, query); - }, + makeHref: deprecatedMethod('makeHref', function (to, params, query) { + return this.context.router.makeHref(to, params, query); + }), /** * Transitions to the URL specified in the arguments by pushing * a new URL onto the history stack. */ - transitionTo: function (to, params, query) { - this.context.transitionTo(to, params, query); - }, + transitionTo: deprecatedMethod('transitionTo', function (to, params, query) { + this.context.router.transitionTo(to, params, query); + }), /** * Transitions to the URL specified in the arguments by replacing * the current URL in the history stack. */ - replaceWith: function (to, params, query) { - this.context.replaceWith(to, params, query); - }, + replaceWith: deprecatedMethod('replaceWith', function (to, params, query) { + this.context.router.replaceWith(to, params, query); + }), /** * Transitions to the previous URL. */ - goBack: function () { - return this.context.goBack(); - } + goBack: deprecatedMethod('goBack', function () { + return this.context.router.goBack(); + }) }; diff --git a/modules/NavigationContext.js b/modules/NavigationContext.js deleted file mode 100644 index f1cf909638..0000000000 --- a/modules/NavigationContext.js +++ /dev/null @@ -1,28 +0,0 @@ -var PropTypes = require('./PropTypes'); - -/** - * Provides the router with context for Router.Navigation. - */ -var NavigationContext = { - - childContextTypes: { - makePath: PropTypes.func.isRequired, - makeHref: PropTypes.func.isRequired, - transitionTo: PropTypes.func.isRequired, - replaceWith: PropTypes.func.isRequired, - goBack: PropTypes.func.isRequired - }, - - getChildContext: function () { - return { - makePath: this.constructor.makePath.bind(this.constructor), - makeHref: this.constructor.makeHref.bind(this.constructor), - transitionTo: this.constructor.transitionTo.bind(this.constructor), - replaceWith: this.constructor.replaceWith.bind(this.constructor), - goBack: this.constructor.goBack.bind(this.constructor) - }; - } - -}; - -module.exports = NavigationContext; diff --git a/modules/PropTypes.js b/modules/PropTypes.js index a4488acbbe..c2cfc7ee6c 100644 --- a/modules/PropTypes.js +++ b/modules/PropTypes.js @@ -1,16 +1,28 @@ var assign = require('react/lib/Object.assign'); var ReactPropTypes = require('react').PropTypes; +var Route = require('./Route'); -var PropTypes = assign({ +var PropTypes = assign({}, ReactPropTypes, { /** - * Requires that the value of a prop be falsy. + * Indicates that a prop should be falsy. */ falsy(props, propName, componentName) { if (props[propName]) - return new Error('<' + componentName + '> may not have a "' + propName + '" prop'); - } + return new Error(`<${componentName}> may not have a "${propName}" prop`); + }, -}, ReactPropTypes); + /** + * Indicates that a prop should be a Route object. + */ + route: ReactPropTypes.instanceOf(Route), + + /** + * Indicates that a prop should be a Router object. + */ + //router: ReactPropTypes.instanceOf(Router) // TODO + router: ReactPropTypes.func + +}); module.exports = PropTypes; diff --git a/modules/RouteHandlerMixin.js b/modules/RouteHandlerMixin.js index a68a0d3921..59a0a0f1f0 100644 --- a/modules/RouteHandlerMixin.js +++ b/modules/RouteHandlerMixin.js @@ -7,9 +7,8 @@ var REF_NAME = '__routeHandler__'; var RouteHandlerMixin = { contextTypes: { - getRouteAtDepth: PropTypes.func.isRequired, - setRouteComponentAtDepth: PropTypes.func.isRequired, - routeHandlers: PropTypes.array.isRequired + routeHandlers: PropTypes.array.isRequired, + router: PropTypes.router.isRequired }, childContextTypes: { @@ -35,7 +34,7 @@ var RouteHandlerMixin = { }, _updateRouteComponent: function (component) { - this.context.setRouteComponentAtDepth(this.getRouteDepth(), component); + this.context.router.setRouteComponentAtDepth(this.getRouteDepth(), component); }, getRouteDepth: function () { @@ -43,7 +42,7 @@ var RouteHandlerMixin = { }, createChildRouteHandler: function (props) { - var route = this.context.getRouteAtDepth(this.getRouteDepth()); + var route = this.context.router.getRouteAtDepth(this.getRouteDepth()); return route ? React.createElement(route.handler, assign({}, props || this.props, { ref: REF_NAME })) : null; } diff --git a/modules/State.js b/modules/State.js index 105a272305..c6bb834950 100644 --- a/modules/State.js +++ b/modules/State.js @@ -1,5 +1,17 @@ +var warning = require('react/lib/warning'); var PropTypes = require('./PropTypes'); +function deprecatedMethod(routerMethodName, fn) { + return function () { + warning( + false, + `Router.State is deprecated. Please use this.context.router.${routerMethodName}() instead` + ); + + return fn.apply(this, arguments); + }; +} + /** * A mixin for components that need to know the path, routes, URL * params and query that are currently active. @@ -8,7 +20,7 @@ var PropTypes = require('./PropTypes'); * * var AboutLink = React.createClass({ * mixins: [ Router.State ], - * render: function () { + * render() { * var className = this.props.className; * * if (this.isActive('about')) @@ -21,56 +33,51 @@ var PropTypes = require('./PropTypes'); var State = { contextTypes: { - getCurrentPath: PropTypes.func.isRequired, - getCurrentRoutes: PropTypes.func.isRequired, - getCurrentPathname: PropTypes.func.isRequired, - getCurrentParams: PropTypes.func.isRequired, - getCurrentQuery: PropTypes.func.isRequired, - isActive: PropTypes.func.isRequired + router: PropTypes.router.isRequired }, /** * Returns the current URL path. */ - getPath: function () { - return this.context.getCurrentPath(); - }, - - /** - * Returns an array of the routes that are currently active. - */ - getRoutes: function () { - return this.context.getCurrentRoutes(); - }, + getPath: deprecatedMethod('getCurrentPath', function () { + return this.context.router.getCurrentPath(); + }), /** * Returns the current URL path without the query string. */ - getPathname: function () { - return this.context.getCurrentPathname(); - }, + getPathname: deprecatedMethod('getCurrentPathname', function () { + return this.context.router.getCurrentPathname(); + }), /** * Returns an object of the URL params that are currently active. */ - getParams: function () { - return this.context.getCurrentParams(); - }, + getParams: deprecatedMethod('getCurrentParams', function () { + return this.context.router.getCurrentParams(); + }), /** * Returns an object of the query params that are currently active. */ - getQuery: function () { - return this.context.getCurrentQuery(); - }, + getQuery: deprecatedMethod('getCurrentQuery', function () { + return this.context.router.getCurrentQuery(); + }), + + /** + * Returns an array of the routes that are currently active. + */ + getRoutes: deprecatedMethod('getCurrentRoutes', function () { + return this.context.router.getCurrentRoutes(); + }), /** * A helper method to determine if a given route, params, and query * are active. */ - isActive: function (to, params, query) { - return this.context.isActive(to, params, query); - } + isActive: deprecatedMethod('isActive', function (to, params, query) { + return this.context.router.isActive(to, params, query); + }) }; diff --git a/modules/StateContext.js b/modules/StateContext.js deleted file mode 100644 index 57c78e8666..0000000000 --- a/modules/StateContext.js +++ /dev/null @@ -1,101 +0,0 @@ -var assign = require('react/lib/Object.assign'); -var PropTypes = require('./PropTypes'); -var PathUtils = require('./PathUtils'); - -function routeIsActive(activeRoutes, routeName) { - return activeRoutes.some(function (route) { - return route.name === routeName; - }); -} - -function paramsAreActive(activeParams, params) { - for (var property in params) - if (String(activeParams[property]) !== String(params[property])) - return false; - - return true; -} - -function queryIsActive(activeQuery, query) { - for (var property in query) - if (String(activeQuery[property]) !== String(query[property])) - return false; - - return true; -} - -/** - * Provides the router with context for Router.State. - */ -var StateContext = { - - /** - * Returns the current URL path + query string. - */ - getCurrentPath: function () { - return this.state.path; - }, - - /** - * Returns a read-only array of the currently active routes. - */ - getCurrentRoutes: function () { - return this.state.routes.slice(0); - }, - - /** - * Returns the current URL path without the query string. - */ - getCurrentPathname: function () { - return this.state.pathname; - }, - - /** - * Returns a read-only object of the currently active URL parameters. - */ - getCurrentParams: function () { - return assign({}, this.state.params); - }, - - /** - * Returns a read-only object of the currently active query parameters. - */ - getCurrentQuery: function () { - return assign({}, this.state.query); - }, - - /** - * Returns true if the given route, params, and query are active. - */ - isActive: function (to, params, query) { - if (PathUtils.isAbsolute(to)) - return to === this.state.path; - - return routeIsActive(this.state.routes, to) && - paramsAreActive(this.state.params, params) && - (query == null || queryIsActive(this.state.query, query)); - }, - - childContextTypes: { - getCurrentPath: PropTypes.func.isRequired, - getCurrentRoutes: PropTypes.func.isRequired, - getCurrentPathname: PropTypes.func.isRequired, - getCurrentParams: PropTypes.func.isRequired, - getCurrentQuery: PropTypes.func.isRequired, - isActive: PropTypes.func.isRequired - }, - - getChildContext: function () { - return { - getCurrentPath: this.getCurrentPath, - getCurrentRoutes: this.getCurrentRoutes, - getCurrentPathname: this.getCurrentPathname, - getCurrentParams: this.getCurrentParams, - getCurrentQuery: this.getCurrentQuery, - isActive: this.isActive - }; - } - -}; - -module.exports = StateContext; diff --git a/modules/TestUtils.js b/modules/TestUtils.js index 1700c68cb8..9573de7280 100644 --- a/modules/TestUtils.js +++ b/modules/TestUtils.js @@ -1,6 +1,6 @@ var React = require('react'); var RouteHandler = require('./components/RouteHandler'); -var State = require('./State'); +var PropTypes = require('./PropTypes'); exports.Nested = React.createClass({ render: function () { @@ -111,8 +111,10 @@ exports.EchoFooProp = React.createClass({ }); exports.EchoBarParam = React.createClass({ - mixins: [ State ], + contextTypes: { + router: PropTypes.router.isRequired + }, render: function () { - return