Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/components/connectAdvanced.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ export default function connectAdvanced(
`or explicitly pass "${storeKey}" as a prop to "${displayName}".`
)

// make sure `getState` is properly bound in order to avoid breaking
// custom store implementations that rely on the store's context
this.getState = this.store.getState.bind(this.store);

this.initSelector()
this.initSubscription()
}
Expand Down Expand Up @@ -159,7 +163,8 @@ export default function connectAdvanced(
}

initSelector() {
const { dispatch, getState } = this.store
const { dispatch } = this.store
const { getState } = this;
const sourceSelector = selectorFactory(dispatch, selectorFactoryOptions)

// wrap the selector in an object that tracks its results between runs
Expand Down
2 changes: 1 addition & 1 deletion src/utils/Subscription.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default class Subscription {
constructor(store, parentSub) {
this.subscribe = parentSub
? parentSub.addNestedSub.bind(parentSub)
: store.subscribe
: store.subscribe.bind(store)

this.unsubscribe = null
this.nextListeners = this.currentListeners = []
Expand Down
48 changes: 48 additions & 0 deletions test/components/connect.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,30 @@ describe('React', () => {
}
}

class ContextBoundStore {
constructor(reducer) {
this.reducer = reducer
this.listeners = []
this.state = undefined
this.dispatch({})
}

getState() {
return this.state
}

subscribe(listener) {
this.listeners.push(listener)
return (() => this.listeners.filter(l => l !== listener))
}

dispatch(action) {
this.state = this.reducer(this.getState(), action)
this.listeners.forEach(l => l())
return action
}
}

ProviderMock.childContextTypes = {
store: PropTypes.object.isRequired
}
Expand Down Expand Up @@ -134,6 +158,30 @@ describe('React', () => {
expect(stub.props.string).toBe('ab')
})

it('should retain the store\'s context', () => {
const store = new ContextBoundStore(stringBuilder)

let Container = connect(
state => ({ string: state })
)(function Container(props) {
return <Passthrough {...props}/>
})

const spy = expect.spyOn(console, 'error')
const tree = TestUtils.renderIntoDocument(
<ProviderMock store={store}>
<Container />
</ProviderMock>
)
spy.destroy()
expect(spy.calls.length).toBe(0)

const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough)
expect(stub.props.string).toBe('')
store.dispatch({ type: 'APPEND', body: 'a' })
expect(stub.props.string).toBe('a')
})

it('should handle dispatches before componentDidMount', () => {
const store = createStore(stringBuilder)

Expand Down