Skip to content

Commit

Permalink
Supporting factory selectors in the object mapToProps argument
Browse files Browse the repository at this point in the history
  • Loading branch information
josepot committed Jun 21, 2017
1 parent 65b7ccd commit ead6fde
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 35 deletions.
2 changes: 2 additions & 0 deletions docs/api.md
Expand Up @@ -56,6 +56,8 @@ It does not modify the component class passed to it; instead, it *returns* a new

If your `mapStateToProps` function is declared as taking two parameters, it will be called with the store state as the first parameter and the props passed to the connected component as the second parameter, and will also be re-invoked whenever the connected component receives new props as determined by shallow equality comparisons. (The second parameter is normally referred to as `ownProps` by convention.)

If an object is passed, each function inside it is assumed to be a Redux selector or a Factory function.

>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.
>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/reactjs/reselect) to efficiently compose selectors and [compute derived data](http://redux.js.org/docs/recipes/ComputingDerivedData.html).
Expand Down
20 changes: 19 additions & 1 deletion src/connect/mapStateToProps.js
@@ -1,11 +1,28 @@
import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'
import {
wrapMapToPropsConstant,
wrapMapToPropsFunc,
wrapMapToPropsObject
} from './wrapMapToProps'

export function whenMapStateToPropsIsFunction(mapStateToProps) {
return (typeof mapStateToProps === 'function')
? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps')
: undefined
}

function isValidmapStateToPropsObj(mapStateToProps) {
return typeof mapStateToProps === 'object' && Object
.keys(mapStateToProps)
.map(function (key) { return mapStateToProps[key] })
.every(function (val) { return typeof val === 'function' })
}

export function whenMapStateToPropsIsObject(mapStateToProps) {
return (isValidmapStateToPropsObj(mapStateToProps)) ?
wrapMapToPropsObject(mapStateToProps) :
undefined
}

export function whenMapStateToPropsIsMissing(mapStateToProps) {
return (!mapStateToProps)
? wrapMapToPropsConstant(() => ({}))
Expand All @@ -14,5 +31,6 @@ export function whenMapStateToPropsIsMissing(mapStateToProps) {

export default [
whenMapStateToPropsIsFunction,
whenMapStateToPropsIsObject,
whenMapStateToPropsIsMissing
]
40 changes: 38 additions & 2 deletions src/connect/wrapMapToProps.js
Expand Up @@ -35,7 +35,7 @@ export function getDependsOnOwnProps(mapToProps) {
// * On first call, verifies the first result is a plain object, in order to warn
// the developer that their mapToProps function is not returning a valid result.
//
export function wrapMapToPropsFunc(mapToProps, methodName) {
export function wrapMapToPropsFunc(mapToProps, methodName, isObjMapToProps = false) {
return function initProxySelector(dispatch, { displayName }) {
const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
return proxy.dependsOnOwnProps
Expand All @@ -57,7 +57,7 @@ export function wrapMapToPropsFunc(mapToProps, methodName) {
props = proxy(stateOrDispatch, ownProps)
}

if (process.env.NODE_ENV !== 'production')
if (process.env.NODE_ENV !== 'production' && !isObjMapToProps)
verifyPlainObject(props, displayName, methodName)

return props
Expand All @@ -66,3 +66,39 @@ export function wrapMapToPropsFunc(mapToProps, methodName) {
return proxy
}
}

function mapObject (obj, mapFn) {
const result = {}
Object
.keys(obj)
.forEach(function(key) { result[key] = mapFn(obj[key], key, obj) })
return result
}

export function wrapMapToPropsObject (mapStateToProps) {
const wrappedMapToProps = mapObject(mapStateToProps, function (fn) {
return wrapMapToPropsFunc(fn, 'mapStateToProps', true)
})

const dependsOnOwnProps = Object
.keys(wrappedMapToProps)
.map(key => wrappedMapToProps[key])
.some(getDependsOnOwnProps)

function initObjectSelector(...initArgs) {
const initializedWraps = mapObject(wrappedMapToProps, function (fn) {
return fn(...initArgs)
})

function objectSelector (...selectorArgs) {
return mapObject(initializedWraps, function (fn) {
return fn(...selectorArgs)
})
}

objectSelector.dependsOnOwnProps = dependsOnOwnProps
return objectSelector
}

return initObjectSelector
}

0 comments on commit ead6fde

Please sign in to comment.