预备知识

回顾一下 Redux 基本用法

const reducer = (state = {count: 0}, action ) => {
  switch(action.type){
  case "INCRESE": return {count: state.count + 1}
  case "DECRESE": return {count: state.count - 1}
  default: return state;
  }
}

const actions = {
  increase: () => { type: 'INCREASE'},
  decrease: () => { type: 'DECREASE'}
}

const store = createStore(reducer);

store.subscribe(()=>{
  console.log(store.getState())
});

store.dispatch(action.increase());
`</pre>

通过 reducer 创建一个 store, 每当我们在 store 上 dispatch 一个 action, store 内的数据就会通过 reducer 变化.

我们当然可以直接在 React 中使用 Redux, 在最外层容器中初始化 store, 然后将 state 上的属性作为 props 层层传下去

<pre>`class App extends Component{
  componentWillMount(){
    store.subscribe((state) =&gt; this.setState(state))
  }
  render(){
    return &lt;Comp state={this.state} onIncrease = {()=&gt;store.dispatch(actions.increase())} onDecrease = {()=&gt; store.dispatch(actions.decrease())}
  } /&gt;
}
`</pre>

但这不是最佳方式, 最佳方式是使用 react-redux 提供的 Provider 和 connect 方法

### 使用 React-redux

首先在最外层容器中, 把所有内容包裹在 Provider 组件中, 将之前创建的 store 作为 prop 传递给 Provider

<pre>`const App = () =&gt; {
  return(
    &lt;Provider store = {store}&gt;
      &lt;Comp /&gt;
    &lt;/Provider&gt;
  )
}
`</pre>

Provider 中的任何一个组件(比如这里的 Comp) 如果需要使用 state 中的数据, 就必须是 `被 connect 过的`组件, 使用 connect 方法对你编写的组件(MyComp)进行包装后的产物.

<pre>`class MyComp extends Component {
  // content...
}

const Comp = connect(...args)(MyComp);
`</pre>

可见 connect 是重中之重

### connect 详解

究竟 connect 做了什么

首先看一下函数的签名:

<pre>`connect([mapStatToProps], [mapDispatchToProps], [mergeProps], [options])
`</pre>

connect() 函数接受四个参数, 分别是

<pre>`mapStatToProps, mapDispatchToProps, mergeProps, options
`</pre>

<pre>`mapStatToProps(state, ownProps): stateProps
`</pre>

这个函数允许我们将 store 中的数据作为 props 绑定到组件上

<pre>`const mapStatToProps = (state) =&gt; {
  return {
    count: state.count
  }
}
`</pre>

这个函数的第一个参数就是 Redux 的 store, 我们从中摘取 count 属性, 因为返回了具有 count 属性的对象, 所以 MyComp 会有名为 count 的 props 字段

<pre>`class MyComp extends Component{
  render(){
    return &lt;div&gt;Count: {this.props.count}&lt;/div&gt;
  }
}

const Comp = connect(...args)(MyComp);
`</pre>

当然你不必将 state 原封不动传递给组件, 可以根据 state 中的数据, 动态的输出组件需要的(最小)属性

<pre>`const mapStateToProps = (state) =&gt; {
  return {
    greaterThanFive: state.count &gt; 5
  }
}
`</pre>

第二个参数 ownProps, 是 MyComp 自己的 props, 有时候 ownProps 也会对其产生影响, 比如当你在 store 中维护一个用户列表, 而你的组件 MyComp 只关心一个用户(通过 props 中的 userId体现)

<pre>`const mapStateToProps = (state, ownProps) =&gt; {
  // state 是{ userList:[{id:0,name;'王二'}]}
  return {
    user: _.find(state.userList, {id:ownProps.userId})
  }
}

class MyComp extends Component{
  static PropTypes = {
    userId: PropTypes.string.isRequired,
    user: PropTypes.object
  };
  render(){
    return &lt;div&gt;UserName: {this.props.user.name}&lt;/div&gt;
  }
}

const Comp = connect(mapStateToProps)(MyComp)
`</pre>

当 state 变化, 或者 ownProps 变化时, mapStateToProps 都会被调用, 计算出一个新的 stateProps , 在与 ownProps merge 后, 更新给 MyComp

这就是将 Redux store 中的数据连接到组件的基本方法

mapDispatchToProps(dispatch, ownProps): dispatchProps

connect 的第二个参数是 mapDispatchToProps, 功能是将 action 作为 props 绑定到 MyComp 上

<pre>`const mapDispatchToProps = (dispatch, ownProps) =&gt; {
  return {
    increase: (...args) =&gt; dispatch(actions.increase(...args)),
    decrease: (...args) =&gt; dispatch(actions.decrease(...args))
  }
}

class MyComp extends Component{
  render(){
    const {count, increase, decrease} = this.props
    return (
      &lt;div&gt;
        &lt;div&gt;Count:{count}&lt;/div&gt;
        &lt;button onClick={increase}&gt;+&lt;/button&gt;
        &lt;button onClick={decrease}&gt;-&lt;/button&gt;
      &lt;/div&gt;
    )
  }
}

const Comp = connect(mapStateToProps, mapDispatchToProps)(MyComp)
`</pre>

由于 mapDispatchToProps 方法返回了具有 increase 属性和 decrease 属性的对象, 这两个属性也会成为 MyComp 的 props

如上所示, 调用 actions.increase()只能得到一个 action 对象{type:'INCREASE'}, 要触发这个 action 必须要通过 dispatch 调用, dispatch 正式 mapDispatchToProps 的第一个参数, 但是为了不让 MyComp 组件感知到 dispatch 的存在, 我们需要将 increase 和 decrease 方法包装一下, 使之成为直接可以被调用的函数(即触发该方法就会触发 dispatch)

Redux 本身提供了 bindActionCreator 函数, 来将 action 包装成可以直接被调用的函数

<pre>`import {bindActionCreator} from 'redux'
const mapDispatchToProps = (dispatch,ownProps) =&gt; {
  return bindActionCreator({
    increase: actions.increase,
    decrease: actions.decrease
  })
}

同样当 ownProps 变化时, 该函数也会被调用, 生成一个新的 dispatchProps( 在与 stateProps 和 ownPorps merge 后), 更新给 MyComp. 注意 action 的变化不会引起上述过程, 默认 action 在组件的生命周期中是固定的

[mergeProps(stateProps, dispatchProps, ownProps): props]

之前说过不管是 stateProps 还是 dispatchProps, 都需要和 ownProps merge 之后才会被赋给 MyComp, connect 的第三个参数就是来做这个事情的, 通常情况下不需要传这个参数, connect 会使用 Object.assign 方法代替该方法.

其他

最后还有一个 options 参数, 比较简单, 一般也不会用到.

参考