在重構 ThemeSwitch
的時候咱們發現,ThemeSwitch
除了須要 store
裏面的數據之外,還須要 store
來 dispatch
:html
... // dispatch action 去改變顏色 handleSwitchColor (color) { const { store } = this.context store.dispatch({ type: 'CHANGE_COLOR', themeColor: color }) } ...
目前版本的 connect
是達不到這個效果的,咱們須要改進它。react
想一下,既然能夠經過給 connect
函數傳入 mapStateToProps
來告訴它如何獲取、整合狀態,咱們也能夠想到,能夠給它傳入另一個參數來告訴它咱們的組件須要如何觸發 dispatch
。咱們把這個參數叫 mapDispatchToProps
:redux
const mapDispatchToProps = (dispatch) => { return { onSwitchColor: (color) => { dispatch({ type: 'CHANGE_COLOR', themeColor: color }) } } }
和 mapStateToProps
同樣,它返回一個對象,這個對象內容會一樣被 connect
看成是 props
參數傳給被包裝的組件。不同的是,這個函數不是接受 state
做爲參數,而是 dispatch
,你能夠在返回的對象內部定義一些函數,這些函數會用到 dispatch
來觸發特定的 action
。app
調整 connect
讓它能接受這樣的 mapDispatchToProps
:ide
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
。接着把 stateProps
、dispatchProps
、this.props
三者合併到 this.state.allProps
裏面去,這三者的內容都會在 render
函數內所有傳給被包裝的組件。函數
另外,咱們稍微調整了一下,在調用 mapStateToProps
和 mapDispatchToProps
以前作判斷,讓這兩個參數都是能夠缺省的,這樣即便不傳這兩個參數程序也不會報錯。this
這時候咱們就能夠重構 ThemeSwitch
,讓它擺脫 store.dispatch
:spa
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
下一節:動手實現 React-redux(五):Providercomponent