预备知识
回顾一下 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) => this.setState(state))
}
render(){
return <Comp state={this.state} onIncrease = {()=>store.dispatch(actions.increase())} onDecrease = {()=> store.dispatch(actions.decrease())}
} />
}
`</pre>
但这不是最佳方式, 最佳方式是使用 react-redux 提供的 Provider 和 connect 方法
### 使用 React-redux
首先在最外层容器中, 把所有内容包裹在 Provider 组件中, 将之前创建的 store 作为 prop 传递给 Provider
<pre>`const App = () => {
return(
<Provider store = {store}>
<Comp />
</Provider>
)
}
`</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) => {
return {
count: state.count
}
}
`</pre>
这个函数的第一个参数就是 Redux 的 store, 我们从中摘取 count 属性, 因为返回了具有 count 属性的对象, 所以 MyComp 会有名为 count 的 props 字段
<pre>`class MyComp extends Component{
render(){
return <div>Count: {this.props.count}</div>
}
}
const Comp = connect(...args)(MyComp);
`</pre>
当然你不必将 state 原封不动传递给组件, 可以根据 state 中的数据, 动态的输出组件需要的(最小)属性
<pre>`const mapStateToProps = (state) => {
return {
greaterThanFive: state.count > 5
}
}
`</pre>
第二个参数 ownProps, 是 MyComp 自己的 props, 有时候 ownProps 也会对其产生影响, 比如当你在 store 中维护一个用户列表, 而你的组件 MyComp 只关心一个用户(通过 props 中的 userId体现)
<pre>`const mapStateToProps = (state, ownProps) => {
// 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 <div>UserName: {this.props.user.name}</div>
}
}
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) => {
return {
increase: (...args) => dispatch(actions.increase(...args)),
decrease: (...args) => dispatch(actions.decrease(...args))
}
}
class MyComp extends Component{
render(){
const {count, increase, decrease} = this.props
return (
<div>
<div>Count:{count}</div>
<button onClick={increase}>+</button>
<button onClick={decrease}>-</button>
</div>
)
}
}
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) => {
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 参数, 比较简单, 一般也不会用到.