Skip to content

Commit 623d511

Browse files
committed
Pass parent props into mapStateToProps
Addresses part of #52 by passing the parent props into mapStateWithProps.
1 parent d13d76a commit 623d511

File tree

3 files changed

+52
-9
lines changed

3 files changed

+52
-9
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ React Redux
44
Official React bindings for [Redux](https://github.com/gaearon/redux).
55
Performant and flexible.
66

7-
[![npm version](https://img.shields.io/npm/v/react-redux.svg?style=flat-square)](https://www.npmjs.com/package/react-redux)
7+
[![npm version](https://img.shields.io/npm/v/react-redux.svg?style=flat-square)](https://www.npmjs.com/package/react-redux)
88
[![npm downloads](https://img.shields.io/npm/dm/react-redux.svg?style=flat-square)](https://www.npmjs.com/package/react-redux)
99
[![redux channel on slack](https://img.shields.io/badge/slack-redux@reactiflux-61DAFB.svg?style=flat-square)](http://www.reactiflux.com)
1010

@@ -226,7 +226,7 @@ Connects a React component to a Redux store.
226226

227227
#### Arguments
228228

229-
* [`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.
229+
* [`mapStateToProps(state, props): stateProps`] \(*Function*): If specified, the component will subscribe to Redux store updates. Any time the store updates or the component receives new props, `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.
230230

231231
* [`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.
232232

@@ -240,7 +240,7 @@ A React component class that injects state and action creators into your compone
240240

241241
* It needs to be invoked two times. First time with its arguments described above, and second time, with the component: `connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent)`.
242242

243-
* The `mapStateToProps` function takes a single argument of the entire Redux store’s state and returns an object to be passed as props. It is often called a **selector**. Use [reselect](https://github.com/faassen/reselect) to efficiently compose selectors and [compute derived data](http://gaearon.github.io/redux/docs/recipes/ComputingDerivedData.html).
243+
* The `mapStateToProps` function takes the entire Redux store’s state and the "smart" component's props and returns an object to be passed as props. It is often called a **selector**. Use [reselect](https://github.com/faassen/reselect) to efficiently compose selectors and [compute derived data](http://gaearon.github.io/redux/docs/recipes/ComputingDerivedData.html).
244244

245245
* **To use `connect()`, the root component of your app must be wrapped into `<Provider>{() => ... }</Provider>` before being rendered.** You may also pass `store` as a prop to the `connect()`ed component, but it's not recommended because it's just too much trouble. Only do this for in non-fully-React codebases or to stub store in a unit test.
246246

@@ -398,7 +398,7 @@ Make sure to check out [Troubleshooting Redux](http://gaearon.github.io/redux/do
398398
### My views aren’t updating!
399399

400400
See the link above.
401-
In short,
401+
In short,
402402

403403
* Reducers should never mutate state, they must return new objects, or React Redux won’t see the updates.
404404
* Make sure you either bind action creators with `mapDispatchToState` argument to `connect()` or with `bindActionCreators()` method, or that you manually call `dispatch()`. Just calling your `MyActionCreators.addTodo()` function won’t work because it just *returns* an action, but not *dispatches* it.

src/components/createConnect.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default function createConnect(React) {
2626
return function connect(mapStateToProps, mapDispatchToProps, mergeProps) {
2727
const shouldSubscribe = Boolean(mapStateToProps);
2828
const finalMapStateToProps = mapStateToProps || defaultMapStateToProps;
29+
const statePropsDependOnParentProps = finalMapStateToProps.length > 1;
2930
const finalMapDispatchToProps = isPlainObject(mapDispatchToProps) ?
3031
wrapActionCreators(mapDispatchToProps) :
3132
mapDispatchToProps || defaultMapDispatchToProps;
@@ -34,9 +35,9 @@ export default function createConnect(React) {
3435
// Helps track hot reloading.
3536
const version = nextVersion++;
3637

37-
function computeStateProps(store) {
38+
function computeStateProps(store, props) {
3839
const state = store.getState();
39-
const stateProps = finalMapStateToProps(state);
40+
const stateProps = finalMapStateToProps(state, props);
4041
invariant(
4142
isPlainObject(stateProps),
4243
'`mapStateToProps` must return an object. Instead received %s.',
@@ -95,15 +96,15 @@ export default function createConnect(React) {
9596
`or explicitly pass "store" as a prop to "${this.constructor.displayName}".`
9697
);
9798

98-
this.stateProps = computeStateProps(this.store);
99+
this.stateProps = computeStateProps(this.store, props);
99100
this.dispatchProps = computeDispatchProps(this.store);
100101
this.state = {
101102
props: this.computeNextState()
102103
};
103104
}
104105

105-
recomputeStateProps() {
106-
const nextStateProps = computeStateProps(this.store);
106+
recomputeStateProps(props = this.props) {
107+
const nextStateProps = computeStateProps(this.store, props);
107108
if (shallowEqual(nextStateProps, this.stateProps)) {
108109
return false;
109110
}
@@ -163,6 +164,9 @@ export default function createConnect(React) {
163164

164165
componentWillReceiveProps(nextProps) {
165166
if (!shallowEqual(nextProps, this.props)) {
167+
if (statePropsDependOnParentProps) {
168+
this.stateProps = computeStateProps(this.store, nextProps);
169+
}
166170
this.recomputeState(nextProps);
167171
}
168172
}

test/components/connect.spec.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,45 @@ describe('React', () => {
375375
expect(div.props.stateThing).toBe('HELLO azbzcZ');
376376
});
377377

378+
it('should pass state and parent props to mapStateToProps', () => {
379+
const store = createStore(stringBuilder);
380+
381+
@connect(
382+
(state, props) => ({idString: `[${props.id}] ${state}`}),
383+
)
384+
class Container extends Component {
385+
render() {
386+
return <div {...this.props}/>;
387+
};
388+
}
389+
390+
class OuterContainer extends Component {
391+
constructor() {
392+
super();
393+
this.state = {id: 0};
394+
}
395+
396+
render() {
397+
return (
398+
<Provider store={store}>
399+
{() => <Container id={this.state.id} />}
400+
</Provider>
401+
);
402+
}
403+
}
404+
405+
const tree = TestUtils.renderIntoDocument(<OuterContainer />);
406+
const div = TestUtils.findRenderedDOMComponentWithTag(tree, 'div');
407+
408+
expect(div.props.idString).toBe('[0] ');
409+
store.dispatch({type: 'APPEND', body: 'a'});
410+
expect(div.props.idString).toBe('[0] a');
411+
tree.setState({id: 1});
412+
expect(div.props.idString).toBe('[1] a');
413+
store.dispatch({type: 'APPEND', body: 'b'});
414+
expect(div.props.idString).toBe('[1] ab');
415+
});
416+
378417
it('should merge actionProps into WrappedComponent', () => {
379418
const store = createStore(() => ({
380419
foo: 'bar'

0 commit comments

Comments
 (0)