Skip to content

Commit dcb1b51

Browse files
committed
Merge pull request #3440 from gaearon/context-mixins
Implement ContextUtils as mixins
2 parents 8d10a89 + c57529b commit dcb1b51

File tree

3 files changed

+45
-57
lines changed

3 files changed

+45
-57
lines changed

modules/ContextUtils.js

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { PropTypes } from 'react'
1+
import { PropTypes } from 'react'
22

33
// Works around issues with context updates failing to propagate.
44
// https://github.com/facebook/react/issues/2517
@@ -13,61 +13,61 @@ function makeContextName(name) {
1313
return `@@contextSubscriber/${name}`
1414
}
1515

16-
export function createContextProvider(name) {
16+
export function ContextProvider(name) {
1717
const contextName = makeContextName(name)
18+
const listenersKey = `${contextName}/listeners`
19+
const eventIndexKey = `${contextName}/eventIndex`
20+
const subscribeKey = `${contextName}/subscribe`
1821

19-
const ContextProvider = React.createClass({
20-
propTypes: {
21-
children: PropTypes.node.isRequired
22-
},
23-
22+
return {
2423
childContextTypes: {
2524
[contextName]: contextProviderShape.isRequired
2625
},
2726

2827
getChildContext() {
2928
return {
3029
[contextName]: {
31-
subscribe: this.subscribe,
32-
eventIndex: this.eventIndex
30+
eventIndex: this[eventIndexKey],
31+
subscribe: this[subscribeKey]
3332
}
3433
}
3534
},
3635

3736
componentWillMount() {
38-
this.eventIndex = 0
39-
this.listeners = []
37+
this[listenersKey] = []
38+
this[eventIndexKey] = 0
4039
},
4140

4241
componentWillReceiveProps() {
43-
this.eventIndex++
42+
this[eventIndexKey]++
4443
},
4544

4645
componentDidUpdate() {
47-
this.listeners.forEach(listener => listener(this.eventIndex))
46+
this[listenersKey].forEach(listener =>
47+
listener(this[eventIndexKey])
48+
)
4849
},
4950

50-
subscribe(listener) {
51+
[subscribeKey](listener) {
5152
// No need to immediately call listener here.
52-
this.listeners.push(listener)
53+
this[listenersKey].push(listener)
5354

5455
return () => {
55-
this.listeners = this.listeners.filter(item => item !== listener)
56+
this[listenersKey] = this[listenersKey].filter(item =>
57+
item !== listener
58+
)
5659
}
57-
},
58-
59-
render() {
60-
return this.props.children
6160
}
62-
})
63-
64-
return ContextProvider
61+
}
6562
}
6663

67-
export function connectToContext(WrappedComponent, name) {
64+
export function ContextSubscriber(name) {
6865
const contextName = makeContextName(name)
66+
const lastRenderedEventIndexKey = `${contextName}/lastRenderedEventIndex`
67+
const handleContextUpdateKey = `${contextName}/handleContextUpdate`
68+
const unsubscribeKey = `${contextName}/unsubscribe`
6969

70-
const ContextSubscriber = React.createClass({
70+
return {
7171
contextTypes: {
7272
[contextName]: contextProviderShape
7373
},
@@ -78,7 +78,7 @@ export function connectToContext(WrappedComponent, name) {
7878
}
7979

8080
return {
81-
lastRenderedEventIndex: this.context[contextName].eventIndex
81+
[lastRenderedEventIndexKey]: this.context[contextName].eventIndex
8282
}
8383
},
8484

@@ -87,8 +87,8 @@ export function connectToContext(WrappedComponent, name) {
8787
return
8888
}
8989

90-
this.unsubscribe = this.context[contextName].subscribe(
91-
this.handleContextUpdate
90+
this[unsubscribeKey] = this.context[contextName].subscribe(
91+
this[handleContextUpdateKey]
9292
)
9393
},
9494

@@ -98,29 +98,23 @@ export function connectToContext(WrappedComponent, name) {
9898
}
9999

100100
this.setState({
101-
lastRenderedEventIndex: this.context[contextName].eventIndex
101+
[lastRenderedEventIndexKey]: this.context[contextName].eventIndex
102102
})
103103
},
104104

105105
componentWillUnmount() {
106-
if (!this.unsubscribe) {
106+
if (!this[unsubscribeKey]) {
107107
return
108108
}
109109

110-
this.unsubscribe()
111-
this.unsubscribe = null
110+
this[unsubscribeKey]()
111+
this[unsubscribeKey] = null
112112
},
113113

114-
handleContextUpdate(eventIndex) {
115-
if (eventIndex !== this.state.lastRenderedEventIndex) {
116-
this.setState({ lastRenderedEventIndex: eventIndex })
114+
[handleContextUpdateKey](eventIndex) {
115+
if (eventIndex !== this.state[lastRenderedEventIndexKey]) {
116+
this.setState({ [lastRenderedEventIndexKey]: eventIndex })
117117
}
118-
},
119-
120-
render() {
121-
return <WrappedComponent {...this.props} />
122118
}
123-
})
124-
125-
return ContextSubscriber
119+
}
126120
}

modules/Link.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react'
22
import { routerShape } from './PropTypes'
3-
import { connectToContext } from './ContextUtils'
3+
import { ContextSubscriber } from './ContextUtils'
44

55
const { bool, object, string, func, oneOfType } = React.PropTypes
66

@@ -41,6 +41,8 @@ function isEmptyObject(object) {
4141
*/
4242
const Link = React.createClass({
4343

44+
mixins: [ ContextSubscriber('router') ],
45+
4446
contextTypes: {
4547
router: routerShape
4648
},
@@ -122,4 +124,4 @@ const Link = React.createClass({
122124

123125
})
124126

125-
export default connectToContext(Link, 'router', object)
127+
export default Link

modules/RouterContext.js

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@ import invariant from 'invariant'
22
import React from 'react'
33

44
import getRouteParams from './getRouteParams'
5-
import { createContextProvider } from './ContextUtils'
5+
import { ContextProvider } from './ContextUtils'
66
import { isReactChildren } from './RouteUtils'
77

88
const { array, func, object } = React.PropTypes
9-
const RouterContextProvider = createContextProvider('router', object.isRequired)
109

1110
/**
1211
* A <RouterContext> renders the component tree for a given router state
1312
* and sets the history object and the current location in context.
1413
*/
1514
const RouterContext = React.createClass({
1615

16+
mixins: [ ContextProvider('router') ],
17+
1718
propTypes: {
1819
router: object.isRequired,
1920
routes: array.isRequired,
@@ -90,21 +91,12 @@ const RouterContext = React.createClass({
9091
}, element)
9192
}
9293

93-
const isEmpty = element === null || element === false
9494
invariant(
95-
isEmpty || React.isValidElement(element),
95+
element === null || element === false || React.isValidElement(element),
9696
'The root route must render a single element'
9797
)
9898

99-
if (isEmpty) {
100-
return element
101-
}
102-
103-
return (
104-
<RouterContextProvider>
105-
{element}
106-
</RouterContextProvider>
107-
)
99+
return element
108100
}
109101

110102
})

0 commit comments

Comments
 (0)