diff --git a/modules/ContextUtils.js b/modules/ContextUtils.js index 03edb36e4e..ab8e7fc85a 100644 --- a/modules/ContextUtils.js +++ b/modules/ContextUtils.js @@ -1,4 +1,4 @@ -import React, { PropTypes } from 'react' +import { PropTypes } from 'react' // Works around issues with context updates failing to propagate. // https://github.com/facebook/react/issues/2517 @@ -13,14 +13,13 @@ function makeContextName(name) { return `@@contextSubscriber/${name}` } -export function createContextProvider(name) { +export function ContextProvider(name) { const contextName = makeContextName(name) + const listenersKey = `${contextName}/listeners` + const eventIndexKey = `${contextName}/eventIndex` + const subscribeKey = `${contextName}/subscribe` - const ContextProvider = React.createClass({ - propTypes: { - children: PropTypes.node.isRequired - }, - + return { childContextTypes: { [contextName]: contextProviderShape.isRequired }, @@ -28,46 +27,47 @@ export function createContextProvider(name) { getChildContext() { return { [contextName]: { - subscribe: this.subscribe, - eventIndex: this.eventIndex + eventIndex: this[eventIndexKey], + subscribe: this[subscribeKey] } } }, componentWillMount() { - this.eventIndex = 0 - this.listeners = [] + this[listenersKey] = [] + this[eventIndexKey] = 0 }, componentWillReceiveProps() { - this.eventIndex++ + this[eventIndexKey]++ }, componentDidUpdate() { - this.listeners.forEach(listener => listener(this.eventIndex)) + this[listenersKey].forEach(listener => + listener(this[eventIndexKey]) + ) }, - subscribe(listener) { + [subscribeKey](listener) { // No need to immediately call listener here. - this.listeners.push(listener) + this[listenersKey].push(listener) return () => { - this.listeners = this.listeners.filter(item => item !== listener) + this[listenersKey] = this[listenersKey].filter(item => + item !== listener + ) } - }, - - render() { - return this.props.children } - }) - - return ContextProvider + } } -export function connectToContext(WrappedComponent, name) { +export function ContextSubscriber(name) { const contextName = makeContextName(name) + const lastRenderedEventIndexKey = `${contextName}/lastRenderedEventIndex` + const handleContextUpdateKey = `${contextName}/handleContextUpdate` + const unsubscribeKey = `${contextName}/unsubscribe` - const ContextSubscriber = React.createClass({ + return { contextTypes: { [contextName]: contextProviderShape }, @@ -78,7 +78,7 @@ export function connectToContext(WrappedComponent, name) { } return { - lastRenderedEventIndex: this.context[contextName].eventIndex + [lastRenderedEventIndexKey]: this.context[contextName].eventIndex } }, @@ -87,8 +87,8 @@ export function connectToContext(WrappedComponent, name) { return } - this.unsubscribe = this.context[contextName].subscribe( - this.handleContextUpdate + this[unsubscribeKey] = this.context[contextName].subscribe( + this[handleContextUpdateKey] ) }, @@ -98,29 +98,23 @@ export function connectToContext(WrappedComponent, name) { } this.setState({ - lastRenderedEventIndex: this.context[contextName].eventIndex + [lastRenderedEventIndexKey]: this.context[contextName].eventIndex }) }, componentWillUnmount() { - if (!this.unsubscribe) { + if (!this[unsubscribeKey]) { return } - this.unsubscribe() - this.unsubscribe = null + this[unsubscribeKey]() + this[unsubscribeKey] = null }, - handleContextUpdate(eventIndex) { - if (eventIndex !== this.state.lastRenderedEventIndex) { - this.setState({ lastRenderedEventIndex: eventIndex }) + [handleContextUpdateKey](eventIndex) { + if (eventIndex !== this.state[lastRenderedEventIndexKey]) { + this.setState({ [lastRenderedEventIndexKey]: eventIndex }) } - }, - - render() { - return } - }) - - return ContextSubscriber + } } diff --git a/modules/Link.js b/modules/Link.js index ee23fd5bad..c658187c7a 100644 --- a/modules/Link.js +++ b/modules/Link.js @@ -1,6 +1,6 @@ import React from 'react' import { routerShape } from './PropTypes' -import { connectToContext } from './ContextUtils' +import { ContextSubscriber } from './ContextUtils' const { bool, object, string, func, oneOfType } = React.PropTypes @@ -41,6 +41,8 @@ function isEmptyObject(object) { */ const Link = React.createClass({ + mixins: [ ContextSubscriber('router') ], + contextTypes: { router: routerShape }, @@ -122,4 +124,4 @@ const Link = React.createClass({ }) -export default connectToContext(Link, 'router', object) +export default Link diff --git a/modules/RouterContext.js b/modules/RouterContext.js index 3199cb6e56..271d813308 100644 --- a/modules/RouterContext.js +++ b/modules/RouterContext.js @@ -2,11 +2,10 @@ import invariant from 'invariant' import React from 'react' import getRouteParams from './getRouteParams' -import { createContextProvider } from './ContextUtils' +import { ContextProvider } from './ContextUtils' import { isReactChildren } from './RouteUtils' const { array, func, object } = React.PropTypes -const RouterContextProvider = createContextProvider('router', object.isRequired) /** * A renders the component tree for a given router state @@ -14,6 +13,8 @@ const RouterContextProvider = createContextProvider('router', object.isRequired) */ const RouterContext = React.createClass({ + mixins: [ ContextProvider('router') ], + propTypes: { router: object.isRequired, routes: array.isRequired, @@ -90,21 +91,12 @@ const RouterContext = React.createClass({ }, element) } - const isEmpty = element === null || element === false invariant( - isEmpty || React.isValidElement(element), + element === null || element === false || React.isValidElement(element), 'The root route must render a single element' ) - if (isEmpty) { - return element - } - - return ( - - {element} - - ) + return element } })