动手实现 React-redux(四):mapDispatchToProps

在重构 ThemeSwitch 的时候咱们发现,ThemeSwitch 除了须要 store 里面的数据之外,还须要 store 来 dispatchhtml

...
  // dispatch action 去改变颜色
  handleSwitchColor (color) {
    const { store } = this.context
    store.dispatch({
      type: 'CHANGE_COLOR',
      themeColor: color
    })
  }
...

目前版本的 connect 是达不到这个效果的,咱们须要改进它。react

想一下,既然能够经过给 connect 函数传入 mapStateToProps 来告诉它如何获取、整合状态,咱们也能够想到,能够给它传入另一个参数来告诉它咱们的组件须要如何触发 dispatch。咱们把这个参数叫 mapDispatchToPropsredux

const mapDispatchToProps = (dispatch) => {
  return {
    onSwitchColor: (color) => {
      dispatch({ type: 'CHANGE_COLOR', themeColor: color })
    }
  }
}

和 mapStateToProps 同样,它返回一个对象,这个对象内容会一样被 connect 看成是 props 参数传给被包装的组件。不同的是,这个函数不是接受 state 做为参数,而是 dispatch,你能够在返回的对象内部定义一些函数,这些函数会用到 dispatch 来触发特定的 actionapp

调整 connect 让它能接受这样的 mapDispatchToPropside

export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
  class Connect extends Component {
    static contextTypes = {
      store: PropTypes.object
    }

    constructor () {
      super()
      this.state = {
        allProps: {}
      }
    }

    componentWillMount () {
      const { store } = this.context
      this._updateProps()
      store.subscribe(() => this._updateProps())
    }

    _updateProps () {
      const { store } = this.context
      let stateProps = mapStateToProps
        ? mapStateToProps(store.getState(), this.props)
        : {} // 防止 mapStateToProps 没有传入
      let dispatchProps = mapDispatchToProps
        ? mapDispatchToProps(store.dispatch, this.props)
        : {} // 防止 mapDispatchToProps 没有传入
      this.setState({
        allProps: {
          ...stateProps,
          ...dispatchProps,
          ...this.props
        }
      })
    }

    render () {
      return <WrappedComponent {...this.state.allProps} />
    }
  }
  return Connect
}

在 _updateProps 内部,咱们把store.dispatch 做为参数传给 mapDispatchToProps,它会返回一个对象 dispatchProps。接着把 statePropsdispatchPropsthis.props 三者合并到 this.state.allProps 里面去,这三者的内容都会在 render函数内所有传给被包装的组件。函数

另外,咱们稍微调整了一下,在调用 mapStateToProps 和 mapDispatchToProps 以前作判断,让这两个参数都是能够缺省的,这样即便不传这两个参数程序也不会报错。this

这时候咱们就能够重构 ThemeSwitch,让它摆脱 store.dispatchspa

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from './react-redux'

class ThemeSwitch extends Component {
  static propTypes = {
    themeColor: PropTypes.string,
    onSwitchColor: PropTypes.func
  }

  handleSwitchColor (color) {
    if (this.props.onSwitchColor) {
      this.props.onSwitchColor(color)
    }
  }

  render () {
    return (
      <div>
        <button
          style={{ color: this.props.themeColor }}
          onClick={this.handleSwitchColor.bind(this, 'red')}>Red</button>
        <button
          style={{ color: this.props.themeColor }}
          onClick={this.handleSwitchColor.bind(this, 'blue')}>Blue</button>
      </div>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    themeColor: state.themeColor
  }
}
const mapDispatchToProps = (dispatch) => {
  return {
    onSwitchColor: (color) => {
      dispatch({ type: 'CHANGE_COLOR', themeColor: color })
    }
  }
}
ThemeSwitch = connect(mapStateToProps, mapDispatchToProps)(ThemeSwitch)

export default ThemeSwitch

光看 ThemeSwitch 内部,是很是清爽干净的,只依赖外界传进来的 themeColor 和 onSwitchColor。可是 ThemeSwitch 内部并不知道这两个参数其实都是咱们去 store里面取的,它是 Dumb 的。这时候这三个组件的重构都已经完成了,代码大大减小、不依赖 context,而且功能和原来同样。code

component

相关文章
相关标签/搜索