react-redux 提供更多了三个关键的第一类, Provider 和 connect ,前者使 React 模块可被相连(connectable),前者把 React 模块和 Redux 的 store 或者说相连出来。react-redux 的文件格式中,对 connect 的叙述是几段艰涩艰涩的英语,在算数 redux 的这时候,我对着那段文件格式写作了很久,都没全数弄知道当中的原意(约莫是,单字我都重新认识,连出来兰香就不知道了的觉得吧)。
在采用了一两年 redux 后,责任编辑试著再度返回这儿,给那段文件格式(与此同时节录在第三章中)三个可信赖的阐释。
有关 react-redux 的三个时序
connect 用语如是说
connect 方式新闻稿:
促进作用:相连 React 模块与 Redux store。
模块表明:
那个表达式容许他们将 store 中的统计数据做为 props 存取到组件上。
(1)那个表达式的第三个模块是 Redux 的 store,他们从中摘取了 count 属性。你不必将 state 中的统计数据原封不动地传入模块,可以根据 state 中的统计数据,动态地输出模块需要的(最小)属性。
(2)表达式的第二个模块 ownProps,是模块自己的 props。有的这时候,ownProps 也会对其产生影响。
当 state 变化,或者 ownProps 变化的这时候,mapStateToProps 都会被调用,计算出三个新的 stateProps,(在与 ownProps merge 后)更新给模块。
connect 的第二个模块是 mapDispatchToProps,它的功能是,将 action 做为 props 存取到模块上,也会成为 MyComp 的 props。
不管是 stateProps 还是 dispatchProps,都需要和 ownProps merge 之后才会被赋给模块。connect 的第三个模块是用来做这件事。通常情况下,你可以不传那个模块,connect 就会采用 Object.assign 替代该方式。
[options] (Object) 如果指定那个模块,可以定制 connector 的行为。一般不用。
基本原理导出
首先connect之所以会成功,是因为Provider模块:
那connect做了些什么呢?
它或者说相连 Redux 和 React,它包在他们的容器模块的外一层,它接收上面 Provider 提供更多的 store 里面的 state 和 dispatch,传给三个构造表达式,返回三个第一类,以属性形式传给他们的容器模块。
有关它的源码
connect 是三个高阶表达式,首先传入 mapStateToProps、mapDispatchToProps,然后返回三个生产 Component 的表达式(wrapWithConnect),然后再将或者说的Component 做为模块传入 wrapWithConnect,这样就生产出三个经过包裹的Connect模块,该模块具有如下特点:
由于 connect 的源码过长,他们只看主要逻辑:
connect 采用实例
这儿他们写三个有关计数器采用的实例:
Component/Counter.js
Container/App.js
首先回顾一下 redux 的基本用语:
[mapStateToProps(state, [ownProps]): stateProps] (Function): 如果定义该模块,模块将会监听 Redux store 的变化。任何这时候,只要 Redux store 发生改变,mapStateToProps 表达式就会被调用。该回调表达式必须返回三个纯第一类,那个第一类会与模块的 props 合并。如果你省略了那个模块,你的模块将不会监听 Redux store。如果指定了该回调表达式中的第二个参数 ownProps,则该模块的值为传递到模块的 props,而且只要模块接收到新的 props,mapStateToProps 也会被调用。
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function): 如果传递的是三个第一类,那么每个定义在该第一类的表达式都将被当作 Redux action creator,而且那个第一类会与 Redux store 存取在一起,当中所定义的方式名将做为属性名,合并到模块的 props 中。如果传递的是三个表达式,该表达式将接收三个 dispatch 表达式,然后由你来决定如何返回三个第一类,那个第一类通过 dispatch 表达式与 action creator 以某种方式存取在一起(提示:你也许会用到 Redux 的辅助表达式 bindActionCreators())。如果你省略那个 mapDispatchToProps 模块,默认情况下,dispatch 会注入到你的模块 props 中。如果指定了该回调表达式中第二个模块 ownProps,该模块的值为传递到模块的 props,而且只要模块接收到新 props,mapDispatchToProps 也会被调用。
[mergeProps(stateProps, dispatchProps, ownProps): props] (Function): 如果指定了那个模块,mapStateToProps() 与 mapDispatchToProps() 的执行结果和模块自身的 props 将传入到那个回调表达式中。该回调表达式返回的第一类将做为 props 传递到被包装的模块中。你也许可以用那个回调表达式,根据模块的 props 来筛选部分的 state 统计数据,或者把 props 中的某个特定变量与 action creator 存取在一起。如果你省略那个模块,默认情况下返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果。
[options] (Object) 如果指定那个模块,可以定制 connector 的行为。
[pure = true] (Boolean): 如果为 true,connector 将执行 shouldComponentUpdate 并且浅对比 mergeProps 的结果,避免不必要的更新,前提是当前模块是三个“纯”模块,它不依赖于任何的输入或 state 而只依赖于 props 和 Redux store 的 state。默认值为 true。[withRef = false] (Boolean): 如果为 true,connector 会保存三个对被包装模块实例的引用,该引用通过 getWrappedInstance() 方式获得。默认值为 false
静态属性
WrappedComponent (Component): 传递到 connect() 表达式的原始模块类。
静态方式
模块原来的静态方式都被提升到被包装的 React 模块。
实例方式
getWrappedInstance(): ReactComponent
仅当 connect() 表达式的第四个模块 options 设置了 { withRef: true } 才返回被包装的模块实例。备注:
1、 表达式将被调用两次。第一次是设置模块,第二次是模块与 Redux store 相连:connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent)。
2、 connect 表达式不会修改传入的 React 模块,返回的是三个新的已与 Redux store 相连的模块,而且你应该采用那个新模块。
3、 mapStateToProps 表达式接收整个 Redux store 的 state 做为 props,然后返回三个传入到模块 props 的第一类。该表达式被称之为 selector。通过 reducer 创建三个 store ,每当他们在 store 上 dispatch 三个 action , store 内的统计数据就会相应地发生变化。
他们当然可以 直接 在 React 中采用 Redux:在最外层容器模块中初始化 store ,然后将 state 上的属性做为 props 层层传递下去。
但这并不是最佳的方式。最佳的方式是采用 react-redux 提供更多的 Provider 和 connect 方式。
采用 react-redux
首先在最外层容器中,把所有内容包裹在 Provider 模块中,将之前创建的 store 做为 prop传给 Provider 。
Provider 内的任何三个模块(比如这儿的 Comp ),如果需要采用 state 中的统计数据,就必须是「被 connect 过的」模块——采用 connect 方式对「你编写的模块( MyComp )」进行包装后的产物。
可见 connect 方式是重中之重。
connect 详解
究竟 connect 方式到底做了什么,他们来一探究竟。
首先看下表达式的签名:
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
connect() 接收四个模块,它们分别是 mapStateToProps , mapDispatchToProps , mergeProps 和 options 。
mapStateToProps(state, ownProps) : stateProps
那个表达式容许他们将 store 中的统计数据做为 props 存取到模块上。
这个表达式的第三个模块是 Redux 的 store ,他们从中摘取了 count 属性。因为返回了具有 count 属性的第一类,所以 MyComp 会有名为 count 的 props 字段。
当然,你不必将 state 中的统计数据原封不动地传入模块,可以根据 state 中的统计数据,动态地输出模块需要的(最小)属性。
表达式的第二个模块 ownProps ,是 MyComp 自己的 props 。有的这时候, ownProps 也会对其产生影响。比如,当你在 store 中维护了三个用户列表,而你的模块 MyComp 只关心三个用户(通过 props 中的 userId 体现)。
当 state 变化,或者 ownProps 变化的这时候, mapStateToProps 都会被调用,计算出三个新的 stateProps ,(在与 ownProps merge 后)更新给 MyComp 。
这是将 Redux store 中的统计数据相连到模块的基本方式。
mapDispatchToProps(dispatch, ownProps): dispatchProps
connect 的第二个模块是 mapDispatchToProps ,它的功能是,将 action 做为 props 存取到 MyComp 上。
由于 mapDispatchToProps 方式返回了具有 increase 属性和 decrease 属性的第一类,这三个属性也会成为 MyComp 的 props 。
如上所示,调用 actions.increase() 只能得到三个 action 第一类 {type:INCREASE} ,要触发那个 action 必须在 store 上调用 dispatch 方式。 diapatch 正是 mapDispatchToProps 的第三个模块。但是,为了不让 MyComp 模块感知到 dispatch 的存在,他们需要将 increase 和 decrease 三个表达式包装一下,使之成为直接可被调用的表达式(即,调用该方式就会触发 dispatch)。
Redux 本身提供更多了 bindActionCreators 表达式,来将 action 包装成直接可被调用的表达式。
同样,当 ownProps 变化的这时候,该表达式也会被调用,生成三个新的 dispatchProps ,(在与 statePrope 和 ownProps merge 后)更新给 MyComp 。注意, action 的变化不会引起上述过程,默认 action 在模块的生命周期中是固定的。
[mergeProps(stateProps, dispatchProps, ownProps): props]
之前说过,不管是 stateProps 还是 dispatchProps ,都需要和 ownProps merge 之后才会被赋给 MyComp 。 connect 的第三个模块是用来做这件事。通常情况下,你可以不传那个模块, connect 就会采用 Object.assign 替代该方式。
其他
最后还有三个 options 选项,比较简单,基本上也不大会用到(尤其是你遵循了其他的一些 React 的「最佳实践」的这时候),责任编辑就略过了。希望了解的同学可以直接看文件格式。