動手實現 React-redux(四):mapDispatchToProps


在重構 ThemeSwitch 的時候我們發現,ThemeSwitch 除了需要 store 里面的數據以外,還需要 store 來 dispatch

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

目前版本的 connect 是達不到這個效果的,我們需要改進它。

想一下,既然可以通過給 connect 函數傳入 mapStateToProps 來告訴它如何獲取、整合狀態,我們也可以想到,可以給它傳入另外一個參數來告訴它我們的組件需要如何觸發 dispatch。我們把這個參數叫 mapDispatchToProps

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

和 mapStateToProps 一樣,它返回一個對象,這個對象內容會同樣被 connect 當作是 props 參數傳給被包裝的組件。不一樣的是,這個函數不是接受 state 作為參數,而是 dispatch,你可以在返回的對象內部定義一些函數,這些函數會用到 dispatch 來觸發特定的 action

調整 connect 讓它能接受這樣的 mapDispatchToProps

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 之前做判斷,讓這兩個參數都是可以缺省的,這樣即使不傳這兩個參數程序也不會報錯。

這時候我們就可以重構 ThemeSwitch,讓它擺脫 store.dispatch

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,並且功能和原來一樣。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM