在重构 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