昨天读了 redux 的源码,今天来看一下在React全家桶里经常和它配对使用的 react-redux
这个源码有点复杂,感觉按我现在的能力完全读懂还是有点难,就先挑着 重点看一下
React-redux 的使用
React-redux 其实就是一个在 react 和 redux 之间架起桥梁的角色,将组件关心的 store state 和 对应的更新方法 作为props绑定到对应的组件上,实现 react 和 redux 之间的隔离
其中起到重要作用的主要有两个部分:Provider 和 connect
connect
1  | function connect(  | 
mapStateToProps
1  | function mapStateToProps(state, ownProps) {  | 
参数中的 state 为 store.state, props 则为组件的props
- 只提供 state 参数——只有state发生变化的时候会重新计算 mapStateToProps
 - state 和 props同时提供——不管props是否用到,只要state发生变化或者 props有变动,都会重新计算 mapStateToProps
 
所以,如果返回对象中不需要 props 进行计算,那么就不要添加这个参数,否则会多很多次mapStateToProps 的计算
与此同时,如果在只提供state的情况下,state没有发生变化,那么是不会重新计算 mapStateToProps 函数的
可以在 mapStateToProps 函数中做一些简单的处理,但是不能太多,因为 mapStateToProps 的计算频率很高,如果很复杂的话会耗费资源
尽可能在需要的时候再返回新的对象引用
react-redux会对 mapStateToProps 的结果做一次浅比较来查看结果是否有改动,然而某些方法很容易在每次都返回新的对象引用,即使真实依赖的对象或数据并没有改动,这样就会导致——即使数据实际上没有改动,但是组件还是会重新渲染。这些方法例如:
- 使用 
someArray.map()或someArray.filter()生成新的 array array.concat()合并数组- 使用 
array.slice()获取array 的一部分 - 使用 
Object.assign()合并对象 - 使用扩展运算符合并对象:
{...oldState, ...newState} 
使用这些方法时要尽量小心,尽可能把他们转移到 redux 的 action 或 reducer中计算,或者在对应组件中根据prop进行计算,尽量减少这种无畏的计算与更新
或者,如果这个计算一定要在 mapStateToProps 中计算的话,建议使用 memoized select Function(?????) 来确保,只有输入真正发生变动的时候才会重新计算mapStateToProps
- 使用 
 
mapDispatchToProps
建立UI组件参数到 store.dispatch 方法的映射,它定义了哪些用户的操作应该当做 Action 传给 Store。可以是一个函数,也可以是一个对象。
函数形式:参数 dispatch + 组件props
1  | const mapDispatchToProps = (  | 
对象形式:
1  | const mapDispatchToProps = {  | 
mergeProps
1  | function mergeProps (stateProps, dispatchProps, ownProps) {  | 
- stateProps为 
mapStateToProps的返回值 - dispatchProps 为 
mapDispatchToProps的返回值 - ownProps 为 组件自身的props
 
以上代码片段中写出的是 默认的 mergeProps 的实现,我们可以覆盖这个函数的返回值,对其进行一些特殊处理(TODO 不大知道实际应用场景是什么)
Options
1  | {  | 
context:
基本没有用过,仿佛是可以用来替换整体 context 的一个东西,但是想要替换的话就必须把 Provider 中和connect中的 context 都换掉
pure:默认为 true
true 则代表,将当前组件看做是一个 pure component,它只依赖于 自身的props 和 connect 中从 Redux.state 中选择的 state,与其它无关
当这个值为 true 的时候,connect 会做一些 额外的检查来判断,是否需要重新执行
mapStateToProps、mapDispatchToProps、mergeProps,并最终决定是否需要执行组件的render方法这些方法为:
areStatesEqual、areOwnPropsEqual、areStatePropsEqual、areMergedPropsEqual这些方法大多数情况下都是会被省略的,但是也可以覆盖来提高效率(并提升看源码的效率)
areStatesEqual:pure为true的情况下,覆盖 state 变动的判断逻辑
areOwnPropsEqual:pure为true的情况下,覆盖 props 变动的判断逻辑(当我们重写了这个方法的时候,通常需要在 mapStateToProps、mapDispatchToProps、mergeProps 中忽略对应的prop属性)
areStatePropsEqual:pure为true的情况下,对
mapStateToProps的结果和其 之前的计算结果进行比对areMergedPropsEqual:pure为true的情况下,对 mergeProps 的结果与其之前的计算结果进行比对
forwardRef:true时,给 connectWrapComponent 添加一个 ref 属性
Provider
connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。
一种解决方法是将state对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state传下去就很麻烦。
React-Redux 提供Provider组件,可以让容器组件拿到state。
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 > import { Provider } from 'react-redux'
> import { createStore } from 'redux'
> import todoApp from './reducers'
> import App from './components/App'
>
> let store = createStore(todoApp);
>
> render(
> <Provider store={store}>
> <App />
> </Provider>,
> document.getElementById('root')
> )
>
>
上面代码中,Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了。
它的原理是React组件的context属性,请看源码。
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 > class Provider extends Component {
> getChildContext() {
> return {
> store: this.props.store
> };
> }
> render() {
> return this.props.children;
> }
> }
>
> Provider.childContextTypes = {
> store: React.PropTypes.object
> }
>