diff --git a/README.md b/README.md index 8eecd77b1..461ab904d 100644 --- a/README.md +++ b/README.md @@ -226,9 +226,9 @@ Connects a React component to a Redux store. #### Arguments -* [`mapStateToProps(state): stateProps`] \(*Function*): If specified, the component will subscribe to Redux store updates. Any time it updates, `mapStateToProps` will be called. Its result must be a plain object, and it will be merged into the component’s props. If you omit it, the component will not be subscribed to the Redux store. +* [`mapStateToProps(state, props): stateProps`] \(*Function*): If specified, the component will subscribe to Redux store updates. Any time it updates, `mapStateToProps` will be called. Its result must be a plain object, and it will be merged into the component’s props. If you omit it, the component will not be subscribed to the Redux store. -* [`mapDispatchToProps(dispatch): dispatchProps`] \(*Object* or *Function*): If an object is passed, each function inside it will be assumed to be a Redux action creator. An object with the same function names, but bound to a Redux store, will be merged into the component’s props. If a function is passed, it will be given `dispatch`. It’s up to you to return an object that somehow uses `dispatch` to bind action creators in your own way. (Tip: you may use [`bindActionCreators()`](http://gaearon.github.io/redux/docs/api/bindActionCreators.html) helper from Redux.) If you omit it, the default implementation just injects `dispatch` into your component’s props. +* [`mapDispatchToProps(dispatch, props): dispatchProps`] \(*Object* or *Function*): If an object is passed, each function inside it will be assumed to be a Redux action creator. An object with the same function names, but bound to a Redux store, will be merged into the component’s props. If a function is passed, it will be given `dispatch`. It’s up to you to return an object that somehow uses `dispatch` to bind action creators in your own way. (Tip: you may use [`bindActionCreators()`](http://gaearon.github.io/redux/docs/api/bindActionCreators.html) helper from Redux.) If you omit it, the default implementation just injects `dispatch` into your component’s props. * [`mergeProps(stateProps, dispatchProps, parentProps): props`] \(*Function*): If specified, it is passed the result of `mapStateToProps()`, `mapDispatchToProps()`, and the parent `props`. The plain object you return from it will be passed as props to the wrapped component. You may specify this function to select a slice of the state based on props, or to bind action creators to a particular variable from props. If you omit it, `{ ...parentProps, ...stateProps, ...dispatchProps }` is used by default. diff --git a/src/components/createConnect.js b/src/components/createConnect.js index 804ca2550..426b42a61 100644 --- a/src/components/createConnect.js +++ b/src/components/createConnect.js @@ -34,9 +34,9 @@ export default function createConnect(React) { // Helps track hot reloading. const version = nextVersion++; - function computeStateProps(context) { + function computeStateProps(context, props = {}) { const state = context.store.getState(); - const stateProps = finalMapStateToProps(state); + const stateProps = finalMapStateToProps(state, props); invariant( isPlainObject(stateProps), '`mapStateToProps` must return an object. Instead received %s.', @@ -45,9 +45,9 @@ export default function createConnect(React) { return stateProps; } - function computeDispatchProps(context) { + function computeDispatchProps(context, props = {}) { const { dispatch } = context.store; - const dispatchProps = finalMapDispatchToProps(dispatch); + const dispatchProps = finalMapDispatchToProps(dispatch, props); invariant( isPlainObject(dispatchProps), '`mapDispatchToProps` must return an object. Instead received %s.', @@ -83,13 +83,13 @@ export default function createConnect(React) { this.version = version; this.setUnderlyingRef = ::this.setUnderlyingRef; - this.stateProps = computeStateProps(context); - this.dispatchProps = computeDispatchProps(context); + this.stateProps = computeStateProps(context, props); + this.dispatchProps = computeDispatchProps(context, props); this.state = this.computeNextState(); } recomputeStateProps() { - const nextStateProps = computeStateProps(this.context); + const nextStateProps = computeStateProps(this.context, this.props); if (shallowEqual(nextStateProps, this.stateProps)) { return false; } @@ -99,7 +99,7 @@ export default function createConnect(React) { } recomputeDispatchProps() { - const nextDispatchProps = computeDispatchProps(this.context); + const nextDispatchProps = computeDispatchProps(this.context, this.props); if (shallowEqual(nextDispatchProps, this.dispatchProps)) { return false; } diff --git a/test/components/connect.spec.js b/test/components/connect.spec.js index ba4d48303..74bdb257f 100644 --- a/test/components/connect.spec.js +++ b/test/components/connect.spec.js @@ -82,6 +82,30 @@ describe('React', () => { ).toNotThrow(); }); + it('should pass the props into the state map', () => { + const store = createStore(() => ({ + foo: 'bar', + baz: 42, + hello: 'world' + })); + + @connect((state, {foo}) => ({ bar: foo })) + class Container extends Component { + render() { + return
; + } + } + + const container = TestUtils.renderIntoDocument( + + {() => } + + ); + + const div = TestUtils.findRenderedDOMComponentWithTag(container, 'div'); + expect(div.props.bar).toEqual(50); + }) + it('should subscribe to the store changes', () => { const store = createStore(stringBuilder);