- 作者:胡子大哈
- 原文链接: http://huziketang.com/books/react/lesson39
- 转载请注明出处,保留原文链接和作者信息。(本文未审核)
在重构 ThemeSwitch 的时候我们发现,ThemeSwitch 除了需要 store 里面的数据以外,还需要 store 来 dispatch:
...// dispatch action 去改变颜色handleSwitchColor (color) {const { store } = this.contextstore.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.contextthis._updateProps()store.subscribe(() => this._updateProps())}_updateProps () {const { store } = this.contextlet 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 之前做判断,让这两个参数都是可以缺省的,这样即使不传这两个参数程序也不会报错。
这时候我们就可以重构 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><buttonstyle={{ color: this.props.themeColor }}onClick={this.handleSwitchColor.bind(this, 'red')}>Red</button><buttonstyle={{ 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,并且功能和原来一样。
因为第三方评论工具有问题,对本章节有任何疑问的朋友可以移步到 React.js 小书的论坛 发帖,我会回答大家的疑问。
