dustinspecker/deku-redux-connect

View on GitHub
src/index.js

Summary

Maintainability
A
1 hr
Test Coverage
import isPlainObj from 'is-plain-obj'
import objectAssign from 'object-assign'

const buildDispatchProps = (dispatch, mapDispatchToProps) => {
  if (isPlainObj(mapDispatchToProps)) {
    return Object.keys(mapDispatchToProps).reduce((acc, action) => {
      if (typeof mapDispatchToProps[action] !== 'function') {
        throw new Error('Expected mapDispatchToProps\' keys to be functions')
      }

      acc[action] = (...args) => dispatch(mapDispatchToProps[action](...args))

      return acc
    }, {})
  }

  if (typeof mapDispatchToProps === 'function') {
    const dispatchProps = mapDispatchToProps(dispatch)

    if (!isPlainObj(dispatchProps)) {
      throw new Error(`Expected mapDispatchToProps to return an object, but got ${dispatchProps}`)
    }

    return dispatchProps
  }

  if (mapDispatchToProps !== undefined) {
    throw new TypeError('Expected mapDispatchToProps to be an Object or Function')
  }
}

const buildStateProps = (context, mapStateToProps) => {
  if (typeof mapStateToProps === 'function') {
    return mapStateToProps(context)
  }

  if (mapStateToProps !== undefined) {
    throw new TypeError('Expected mapStateToProps to be a Function')
  }
}

const transformProps = (props, context, dispatch, mapStateToProps, mapDispatchToProps, mergeProps) => {
  const dispatchProps = buildDispatchProps(dispatch, mapDispatchToProps)
    , stateProps = buildStateProps(context, mapStateToProps)
    , ownProps = props || {}

  if (typeof mergeProps === 'function') {
    return mergeProps(stateProps, dispatchProps, ownProps)
  }

  return objectAssign({}, ownProps, stateProps, dispatchProps)
}

module.exports = (mapStateToProps, mapDispatchToProps, mergeProps) => component => {
  if (typeof component === 'function') {
    // return component function with inject args
    const convertedComponentFunction = ({children, context, dispatch, path, props}) => {
      const transformedProps = transformProps(props, context, dispatch, mapStateToProps, mapDispatchToProps, mergeProps)

      return component({children, dispatch, path, props: transformedProps})
    }

    Object.keys(component).forEach(key => {
      convertedComponentFunction[key] = component[key]
    })

    return convertedComponentFunction
  }

  if (typeof component === 'object') {
    const componentWithModifiedRender = {
      // invoke component render with injected args
      render({children, context, dispatch, path, props}) {
        const transformedProps = transformProps(props, context, dispatch, mapStateToProps,
          mapDispatchToProps, mergeProps)

        return component.render({children, dispatch, path, props: transformedProps})
      }
    }
    // copy component's properties to componentWithModifiedRender
    Object.keys(component).forEach(key => {
      if (key !== 'render') {
        componentWithModifiedRender[key] = component[key]
      }
    })

    return componentWithModifiedRender
  }

  throw new TypeError('Expected component to be an Object or Function')
}