Skip to content

Commit 9a4cda1

Browse files
jquensetimdorr
authored andcommitted
Duck-type subscription in prop checks (reduxjs#628)
* Duck-type subscription in prop checks Use `PropTypes.shape` instead of instanceOf checks which don't work across multiple instances of react-redux. Granted it's not a problem that _should_ occur since they should be deduped, but dependency management is hard, and sometimes that's not feasible due to symlinks and the like. This is (I think) is more idiomatic JS any way :) thanks! (sorry if something obvious is off i wrote this in the GH editor) * Centralize PropType shapes.
1 parent 9f38554 commit 9a4cda1

File tree

4 files changed

+24
-19
lines changed

4 files changed

+24
-19
lines changed

src/components/Provider.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Component, PropTypes, Children } from 'react'
2-
import Subscription from '../utils/Subscription'
3-
import storeShape from '../utils/storeShape'
2+
import { storeShape, subscriptionShape } from '../utils/PropTypes'
43
import warning from '../utils/warning'
54

65
let didWarnAboutReceivingStore = false
@@ -51,6 +50,6 @@ Provider.propTypes = {
5150
}
5251
Provider.childContextTypes = {
5352
store: storeShape.isRequired,
54-
storeSubscription: PropTypes.instanceOf(Subscription)
53+
storeSubscription: subscriptionShape
5554
}
5655
Provider.displayName = 'Provider'

src/components/connectAdvanced.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import hoistStatics from 'hoist-non-react-statics'
22
import invariant from 'invariant'
3-
import { Component, PropTypes, createElement } from 'react'
3+
import { Component, createElement } from 'react'
44

55
import Subscription from '../utils/Subscription'
6-
import storeShape from '../utils/storeShape'
6+
import { storeShape, subscriptionShape } from '../utils/PropTypes'
77

88
let hotReloadingVersion = 0
99
const dummyState = {}
1010
function noop() {}
11-
1211
function makeSelectorStateful(sourceSelector, store) {
1312
// wrap the selector in an object that tracks its results between runs.
1413
const selector = {
@@ -81,10 +80,10 @@ export default function connectAdvanced(
8180

8281
const contextTypes = {
8382
[storeKey]: storeShape,
84-
[subscriptionKey]: PropTypes.instanceOf(Subscription),
83+
[subscriptionKey]: subscriptionShape,
8584
}
8685
const childContextTypes = {
87-
[subscriptionKey]: PropTypes.instanceOf(Subscription)
86+
[subscriptionKey]: subscriptionShape,
8887
}
8988

9089
return function wrapWithConnect(WrappedComponent) {
@@ -194,16 +193,16 @@ export default function connectAdvanced(
194193

195194
initSubscription() {
196195
if (!shouldHandleStateChanges) return
197-
196+
198197
// parentSub's source should match where store came from: props vs. context. A component
199198
// connected to the store via props shouldn't use subscription from context, or vice versa.
200199
const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey]
201200
this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this))
202-
201+
203202
// `notifyNestedSubs` is duplicated to handle the case where the component is unmounted in
204203
// the middle of the notification loop, where `this.subscription` will then be null. An
205204
// extra null check every change can be avoided by copying the method onto `this` and then
206-
// replacing it with a no-op on unmount. This can probably be avoided if Subscription's
205+
// replacing it with a no-op on unmount. This can probably be avoided if Subscription's
207206
// listeners logic is changed to not call listeners that have been unsubscribed in the
208207
// middle of the notification loop.
209208
this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription)
@@ -218,7 +217,7 @@ export default function connectAdvanced(
218217
this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate
219218
this.setState(dummyState)
220219
}
221-
}
220+
}
222221

223222
notifyNestedSubsOnComponentDidUpdate() {
224223
// `componentDidUpdate` is conditionally implemented when `onStateChange` determines it

src/utils/PropTypes.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { PropTypes } from 'react'
2+
3+
export const subscriptionShape = PropTypes.shape({
4+
trySubscribe: PropTypes.func.isRequired,
5+
tryUnsubscribe: PropTypes.func.isRequired,
6+
notifyNestedSubs: PropTypes.func.isRequired,
7+
isSubscribed: PropTypes.func.isRequired,
8+
})
9+
10+
export const storeShape = PropTypes.shape({
11+
subscribe: PropTypes.func.isRequired,
12+
dispatch: PropTypes.func.isRequired,
13+
getState: PropTypes.func.isRequired
14+
})

src/utils/storeShape.js

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)