diff --git a/docs/api.md b/docs/api.md
index edbe3b0db..b14ba3955 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -60,6 +60,8 @@ Instead, it *returns* a new, connected component class, for you to use.
* [`mapStateToProps(state, [ownProps]): 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. If `ownProps` is specified as a second argument, its value will be the props passed to your component, and `mapStateToProps` will be re-invoked whenever the component receives new props.
+ >Note: if your mapStateToProps do not need to access component properties, you can use a shorthand syntax by passing an object whose values are "selectors" (See [reselect](https://github.com/reactjs/reselect))
+
>Note: in advanced scenarios where you need more control over the rendering performance, `mapStateToProps()` can also return a function. In this case, *that* function will be used as `mapStateToProps()` for a particular component instance. This allows you to do per-instance memoization. You can refer to [#279](https://github.com/reactjs/react-redux/pull/279) and the tests it adds for more details. Most apps never need this.
* [`mapDispatchToProps(dispatch, [ownProps]): 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 with every action creator wrapped into a `dispatch` call so they may be invoked directly, 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 the [`bindActionCreators()`](http://reactjs.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. If `ownProps` is specified as a second argument, its value will be the props passed to your component, and `mapDispatchToProps` will be re-invoked whenever the component receives new props.
@@ -239,6 +241,23 @@ function mapDispatchToProps(dispatch) {
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
```
+##### Inject `todos` with shorthand syntax, and all todoActionCreators and counterActionCreators directly as props
+
+```js
+import * as todoActionCreators from './todoActionCreators'
+import * as counterActionCreators from './counterActionCreators'
+import { bindActionCreators } from 'redux'
+
+
+const todosSelector = state => state.todos
+
+function mapDispatchToProps(dispatch) {
+ return bindActionCreators(Object.assign({}, todoActionCreators, counterActionCreators), dispatch)
+}
+
+export default connect({todos: todosSelector}, mapDispatchToProps)(TodoApp)
+```
+
##### Inject `todos` of a specific user depending on props
```js
diff --git a/src/components/connect.js b/src/components/connect.js
index 3b60ebbce..29555f02f 100644
--- a/src/components/connect.js
+++ b/src/components/connect.js
@@ -3,6 +3,7 @@ import storeShape from '../utils/storeShape'
import shallowEqual from '../utils/shallowEqual'
import wrapActionCreators from '../utils/wrapActionCreators'
import warning from '../utils/warning'
+import wrapMapStateObject from '../utils/wrapMapStateObject'
import isPlainObject from 'lodash/isPlainObject'
import hoistStatics from 'hoist-non-react-statics'
import invariant from 'invariant'
@@ -32,9 +33,18 @@ function tryCatch(fn, ctx) {
// Helps track hot reloading.
let nextVersion = 0
+function handleShorthandSyntax(mapStateToProps) {
+ if ( mapStateToProps !== null && typeof mapStateToProps === 'object' ) {
+ return wrapMapStateObject(mapStateToProps)
+ }
+ else {
+ return mapStateToProps
+ }
+}
+
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
const shouldSubscribe = Boolean(mapStateToProps)
- const mapState = mapStateToProps || defaultMapStateToProps
+ const mapState = handleShorthandSyntax(mapStateToProps) || defaultMapStateToProps
let mapDispatch
if (typeof mapDispatchToProps === 'function') {
diff --git a/src/utils/wrapMapStateObject.js b/src/utils/wrapMapStateObject.js
new file mode 100644
index 000000000..d1d4a13c8
--- /dev/null
+++ b/src/utils/wrapMapStateObject.js
@@ -0,0 +1,29 @@
+import invariant from 'invariant'
+
+
+function mapValues(obj, fn) {
+ return Object.keys(obj).reduce((result, key) => {
+ result[key] = fn(obj[key], key)
+ return result
+ }, {})
+}
+
+export default function wrapMapStateObject(mapStateToProps) {
+
+ const needsProps = Object.keys(mapStateToProps)
+ .reduce((useProps, key) => {
+ const type = typeof mapStateToProps[key]
+ invariant(
+ type === 'function',
+ 'mapStateToProps object key %s expected to be a function, instead saw %s',
+ key,
+ type
+ )
+ return useProps || mapStateToProps[key].length !== 1
+ }, false)
+
+ return needsProps
+ ? (state, props) => mapValues(mapStateToProps, fn => fn(state, props))
+ : state => mapValues(mapStateToProps, fn => fn(state)
+ )
+}
diff --git a/test/components/connect.spec.js b/test/components/connect.spec.js
index 514eea018..37d2c2451 100644
--- a/test/components/connect.spec.js
+++ b/test/components/connect.spec.js
@@ -82,6 +82,54 @@ describe('React', () => {
).toNotThrow()
})
+ it('should pass state to given component, with shorthand syntax', () => {
+ const store = createStore(() => ({
+ foo: 'bar',
+ baz: 42,
+ hello: 'world'
+ }))
+
+ @connect({
+ foo: state => state.foo,
+ baz: state => state.baz
+ })
+ class Container extends Component {
+ render() {
+ return