redux学习理解07

redux-study

Posted by EWL on December 15, 2018

react-redux的学习实现第三课:由connect完成事件触发,拒绝组件直接调用store内部方法

上一节的学习中,我们在themeSwitch中去切换组件的颜色时,仍然是通过Index上的context去获取store,然后调用store.dispatch去触发事件,回顾代码如下:

handleSwitchColor = (color) => {
    const { store } = this.context;
    store.dispatch({
      type: 'CHANGE_COLOR',
      themeColor: color
    });
  };

本节的学习,就是要跟着react小书去修改themeSwitch组件派发action的方式。 回忆上一节我们给connect传入的mapStateToProps,这个函数的主要目的就是在确定的某一个组件中,确定的向store中的state索取该组件需要的状态值,官方一点的说法就是如何整合/获取状态。那么同样的我们可以再给connect传入一个mapDispatchToProps,来告诉我们的组件如何触发dispatch。 对比之前的mapStateToProps,我们的mapDispatchToProps的代码大致如下:

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

将上述代码添加进connect的代码,并做调整。

import React, { Component } from 'react';
import PropTypes from 'prop-types';

export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => (
  class 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;
      console.log('this.props', this.props);
      let stateProps = mapStateToProps ? mapStateToProps(store.getState(), this.props) : {};
      let dispatchProps = mapDispatchToProps ? mapDispatchToProps(store.dispatch, this.props) : {};
      this.setState({
        allProps: { // 整合普通的 props 和从 state 生成的 props
          ...stateProps,
          ...this.props,
          ...dispatchProps,
        }
      }, () => {
        console.log('this.state.allProps', this.state.allProps);
      });

    }

    render() {
      return <WrappedComponent {...this.state.allProps}/>
    }
  }
);

这是,我们就可以开始正式去修改ThemeSwitch的内容了。

import React, { Component } from 'react';
import PropTypes from "prop-types";
import { connect } from './react-redux';

class ThemeSwitch extends Component {
  constructor(props) {
    super(props);
    this.state = {
      themeColor: ''
    };
  }
  // static contextTypes = {
  //   store: PropTypes.object
  // };

  static propTypes = {
    handleSwitchColor: PropTypes.func,
    themeColor: PropTypes.string
  };

  // componentWillMount() {
  //   const { store } = this.context;
  //   this._updateThemeColor();
  //   store.subscribe(this._updateThemeColor);
  // }
  //
  // _updateThemeColor = () => {
  //   const { store } = this.context;
  //   const state = store.getState();
  //
  //   this.setState({
  //     themeColor: state.themeColor
  //   });
  // };

  // handleSwitchColor = (color) => {
  //   const { store } = this.context;
  //   store.dispatch({
  //     type: 'CHANGE_COLOR',
  //     themeColor: color
  //   });
  // };


  render() {
    return (
      <div>
        <button
          // style=
          style=
          // onClick={() => this.handleSwitchColor('red')}
          onClick={() => this.props.handleSwitchColor('red')}
        >red</button>
        <button
          // style=
          style=
          // onClick={() => this.handleSwitchColor('blue')}
          onClick={() => this.props.handleSwitchColor('blue')}
        >blue</button>
      </div>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    themeColor: state.themeColor
  }
};

const mapDispatchToProps = (dispatch) => {
  return {
    handleSwitchColor: (color) => {
      dispatch({ type: 'CHANGE_COLOR', themeColor: color })
    }
  }
};
ThemeSwitch = connect(mapStateToProps, mapDispatchToProps)(ThemeSwitch);

export default ThemeSwitch;

其中有一些被注释的地方,明显对比起来,组件内部的代码少了很多,我们将控制状态和控制函数操作的部分,传给connect,让connect将组件和他们连接起来,这样我们就有了一个干净的纯函数组件,我们只关心传入的内容,而不用在组件内容去调用store中的方法,这样大大提高了组件的复用性。

本节总结:

  1. 要时刻关注mapStateToProps和mapDispatchToProps的取值问题,它们并不是时时刻刻都需要的,所以要做合适的回退判断,如果不存在,我们就返回一个空对象,这一点尤为重要
  2. 每一次都要注意修改静态的propTypes,contextTypes,childContextTypes的修改,这上面很容易报错