動手實現 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

相關文章
相關標籤/搜索