首先先强调一句:一定要多读官方文档,而且要精读,否则你会忽略掉很多东西!
一,Provider
刚开始看的时候,大致浏览了一下,知道了这个组件是能够接收store作为它的属性,然后它里面的子组件就可以通过props访问到store里面的所有东西了(好方便啊),然后迫不及待的写了下,发现里面的子组件的props竟然是空的(mmp),再去看一遍文档,发现,一定要用connect连接的组件才能通过props访问到store,没有用的就不能访问到了!!所以一定要看仔细了。
二,connect
connect这个函数其实最终会返回一个包着渲染组件的高阶组件。
它的基础作用如下:
1、从context里获取store
2、在componentWillMount 里通过mapStateToProps获取stateProp的值
3、在componentWillMount 里通过mapDispatchToProps获取dispatchProps的值
4、在componentWillMount 里订阅store的变化
5、将获得的stateProp,dispatchProps,还有自身的props合成一个props传给下面的组件
connect这个方法,官方的用法是:
connect([mapStateToProps], [mapDispatchToProps], [mergeProps])(MyComponent);
所以可以很明显的看出来,第一个括号里可以为空,因为都不是必选的,第二个括号里是你要连接的组件类名,所以最简单的就是
connect()(MyComponent);
这样在组件里就可以直接使用store里面的dispatch方法或者访问到store树里的state了,具体的就是this.props.dispatch()或者this.props.state()
但是这里要注意一点,connect可以不传任何参数,但是如果要使用mapDispatchToProps,就必须传mapStateToProps,否则就会报dispatch is not defined的错,如果mapStateToProps为空,可以直接传一个null
connect(null, mapDispatchToProps)(MyComponent);
接下来就是connect第一个括号里的最重要的两个参数了(mapStateToProps和mapDispatchToProps),在那之前首先弄清楚两个概念,容器组件和展示组件:
容器组件:通常是指最外层的组件,由路由直接控制的,可以使用redux,使用store,使用各种改变状态数据的方法
展示组件:通常是指拆分的小组件,它没有redux,没有store,更不会定义自己的方法,它所有的state都不会从store里面拿,而是由父组件传给他的props里拿,他的动作方法也是由props里面传的,所以它是一个比较“纯净”的组件,你传什么,他展示什么。具体的话一般都是那种复用型的组件,比如标题栏,loading组件这些。
那么什么时候才用传这两个参数,通常是展示组件connect的时候才会传这两个参数。
我们为什么要区分容器组件和展示组件?
1,分离关注,你可以更好的理解app和UI。
2,更易复用,同样的展示组件可以在不同的状态源、数据源中使用。也可以封装成容器组件,在未来重用它们。
3,展示组件是app的调色板。你可以把它们放到单独的页面,并让设计师来调整它们的样式和结构,而不用改变app的逻辑。单独的页面有静态性,你可以在上面进行screenshot regression测试。
4,这种方法会强迫你去解析布局相关的组件,比如Sidebar, Page, ContextMenu,强迫你去使用this.props.children,而非在不同容器中不断复制jsx那块地方。
三,mapStateToProps
类似于vuex里的mapState方法,具体使用直接上代码
function mapStateToProps(state) {
return {
value: state.count.value
}
}
// 此时store树里的initState结构为:
const initState = {
...
count: {
value: ""
}
...
}
那么此时我们要在当前展示组件中使用value的时候就可以通过this.props.value来访问到了,因为此时value已经绑定在了当前组件的props上面,当store里面的state变化的时候,展示组件会同步更新
四,mapDispatchToProps
类似于mapStateToProps,把当前组件所需要的动作也全部定义到props里面,因为通常动作执行的时候都需要dispatch(action),所以我们直接把动作定义好,然后组件直接用就可以了,就不用再去dispatch一个action了,mapDispatchToProps全部帮你绑好了,直接上代码
import {increaseAction} from '../actions'
function mapDispatchToProps(dispatch) {
return {
onIncreaseClick: () => dispatch(increaseAction)
}
}
这里我们就可以直接在按钮上绑定这个方法,onClick={this.props.onIncreaseClick},如果要传参数,那就在()里面加data参数,action后面也跟一个data
要使用mapDispatchToProps必须要在connect里传递mapStateToProps,如果没有要用的,就传一个null,再说一遍!
五,bindActionCreators
这个方法是redux的,把它放在这里讲是因为它搭配mapDispatchToProps比较合适,先看一下它如何替代上面的代码:
import {increaseAction} from '../actions'
function mapDispatchToProps(dispatch) {
return {
onIncreaseClick: bindActionCreators(increaseAction, dispatch)
}
}
bindActionCreators会自动给第一个参数action执行dispatch方法,这里似乎看不出它的方便,但是试想一下,如果此时有n多个方法要定义呢,照着第一种方法,那就要写n多行的dispatch(action)了,但是如果有了bindActionCreators方法,就可以直接传一个所有actions的大对象,直接上代码:
import actions from '../actions/actionCreator'
import {bindActionCreators} from 'redux' function mapDispatchToProps(dispatch) {
return {
dispatchActions: bindActionCreators(actions, dispatch)
}
}
这里的actions是actionCreator文件里所有的action的一个合集,那么此时要在组件中直接使用dispatch(loginAction)就可以用this.props.dispatchActions.loginAction(),有data就传data。
那什么情况下用bindActionCreators,什么时候不用只用第一种呢,我觉得看情况吧,如果当前组件的action比较多的话,可以把当前组件的所有action全部放在一个文件里,然后集体导出来,再用bindActionCreators,如果当前组件就一两个action,那我觉得没必要,直接用第一种就可以了。