• 阅读react-redux源码 - 零
  • 阅读react-redux源码 - 一
  • 阅读react-redux源码(二) - createConnect、match函数的实现
  • 阅读react-redux源码(三) - mapStateToPropsFactories、mapDispatchToPropsFactories和mergePropsFactories
  • 阅读react-redux源码(四) - connectAdvanced、wrapWithConnect、ConnectFunction和checkForUpdates
  • 阅读react-redux源码(五) - connectAdvanced中store改变的事件转发、ref的处理和pure模式的处理
  • 阅读react-redux源码(六) - selectorFactory处理store更新

store中的无关变动就是通过selectorFactory来阻止的。store中的state有很多,而当前组件关注的state不会是全部,例如state:{a: 1, b:2}。组件只关注属性a,但是属性b修改了,因为store.subscribe监听的整个state变化,state确实变化了,但是我关注的部分没有变,也就是b: 2没变,所以当前业务组件如果是pure模式则不应该更新,其中的处理逻辑则在selectorFactory.js中。

顶部函数很简单:

export default function finalPropsSelectorFactory(dispatch,{ initMapStateToProps, initMapDispatchToProps, initMergeProps, ...options }
) {const mapStateToProps = initMapStateToProps(dispatch, options)const mapDispatchToProps = initMapDispatchToProps(dispatch, options)const mergeProps = initMergeProps(dispatch, options)if (process.env.NODE_ENV !== 'production') {verifySubselectors(mapStateToProps,mapDispatchToProps,mergeProps,options.displayName)}const selectorFactory = options.pure? pureFinalPropsSelectorFactory: impureFinalPropsSelectorFactoryreturn selectorFactory(mapStateToProps,mapDispatchToProps,mergeProps,dispatch,options)
}

上一章看到的内容是通过createChildSelector的调用会返回一个childSelector,而childSelector的调用需要入参state和wrapperProps,然后融合成一个整体的props传递给被包裹的组件。

function createChildSelector(store) {return selectorFactory(store.dispatch, selectorFactoryOptions)
}

调用的就是方法finalPropsSelectorFactory得到的返回值是:

selectorFactory(mapStateToProps,mapDispatchToProps,mergeProps,dispatch,options
)

会被如此调用:

childPropsSelector = createChildSelector(store)
childPropsSelector(store.getState(), wrapperProps)

childPropsSelector(store.getState(), wrapperProps)的掉用会返回一个实际的会传递给被包裹的业务组件的完整的props。

是什么和怎么用交代清楚,开始查看代码,如何实现这些功能的。

上面的代码需要处理两种情况,一种是options中的pure设置为ture的pure模式,一种是非pure模式,整个代码需要处理这两种情况,而当前代码的处理方式逻辑十分清晰。
两种模式并不是完全不能复用代码,例如当前的组装各个部分的逻辑就是相同的,而各个被抽出来的部分也是公用的,例如:mapStateToProps、mapDispatchToProps、mergeProps、dispatch、options,这些都是共用的。两种模式都是通过这些入参来决定被合成的props的。所以处理两种条件分支的情况提取他们公共的部分然后通过入参的方式传入组装不失为一个好的方法。

顶部函数finalPropsSelectorFactory的目的很明显,就是为了组装这些参数得到一个selector用于计算返回真正的props。

如果不是pure模式则很简单是一个合并三方的函数,分别合并了mapStateToProps的返回值,mapDispatchToProps的返回值和ownProps,父元素传递给子元素的props,实现方法如下:

export function impureFinalPropsSelectorFactory(mapStateToProps,mapDispatchToProps,mergeProps,dispatch
) {return function impureFinalPropsSelector(state, ownProps) {return mergeProps(mapStateToProps(state, ownProps),mapDispatchToProps(dispatch, ownProps),ownProps)}
}

其中默认的mergeProps函数的实现很简单:

export function defaultMergeProps(stateProps, dispatchProps, ownProps) {return { ...ownProps, ...stateProps, ...dispatchProps }
}

就是一个合并三方返回合并结果的函数。

主要看pureFinalPropsSelectorFactory的实现,在这个函数中封装了重要的内容,完成对比阻止store更新引起的不必要更新的核心就在这个函数里面。

export function pureFinalPropsSelectorFactory(mapStateToProps,mapDispatchToProps,mergeProps,dispatch,{ areStatesEqual, areOwnPropsEqual, areStatePropsEqual }
) {let hasRunAtLeastOnce = falselet statelet ownPropslet statePropslet dispatchPropslet mergedPropsfunction handleFirstCall(firstState, firstOwnProps) { ... }function handleNewPropsAndNewState() { ... }function handleNewProps() { ... }function handleSubsequentCalls(nextState, nextOwnProps) { ... }return function pureFinalPropsSelector(nextState, nextOwnProps) {return hasRunAtLeastOnce? handleSubsequentCalls(nextState, nextOwnProps): handleFirstCall(nextState, nextOwnProps)}
}

pureFinalPropsSelectorFactory 函数返回一个函数pureFinalPropsSelector。这个函数的运行又分为两种情况,之中是第一次运行,一种是非第一次运行。第一次运行执行函数 handleSubsequentCalls(nextState, nextOwnProps),非第一次运行则运行 handleFirstCall(nextState, nextOwnProps)。

handleFirstCall函数的实现:

function handleFirstCall(firstState, firstOwnProps) {state = firstStateownProps = firstOwnPropsstateProps = mapStateToProps(state, ownProps)dispatchProps = mapDispatchToProps(dispatch, ownProps)mergedProps = mergeProps(stateProps, dispatchProps, ownProps)hasRunAtLeastOnce = truereturn mergedProps}

存储一些原始数据,例如firstState、firstOwnProps和计算出来的数据,例如:stateProps、dispatchProps和mergedProps。最后将至少执行一次设置为true,并且返回所有数据的merge结果。

因为hasRunAtLeastOnce为true所以之后执行方法pureFinalPropsSelector真正执行的则是handleSubsequentCalls。

function handleSubsequentCalls(nextState, nextOwnProps) {const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)const stateChanged = !areStatesEqual(nextState, state)state = nextStateownProps = nextOwnPropsif (propsChanged && stateChanged) return handleNewPropsAndNewState()if (propsChanged) return handleNewProps()if (stateChanged) return handleNewProps()return mergedProps}

首先对比下是props变了,还是state变了,还是props和state都变了。

如果都变了执行handleNewPropsAndNewState,如果props变了执行handleNewProps,最后handleNewProps。

其中对比方法默认是:

areStatesEqual = strictEqual,
areOwnPropsEqual = shallowEqual,
areStatePropsEqual = shallowEqual,
areMergedPropsEqual = shallowEqual,

如果都变了,handleNewPropsAndNewState:

function handleNewPropsAndNewState() {stateProps = mapStateToProps(state, ownProps)if (mapDispatchToProps.dependsOnOwnProps)dispatchProps = mapDispatchToProps(dispatch, ownProps)mergedProps = mergeProps(stateProps, dispatchProps, ownProps)return mergedProps}

重新计算mergedProps并返回。

如果只有新的props变了,如果mapStateToProps和mapDispatchToProps依赖props则需要重新计算stateProps和dispatchProps,然后重新计算mergedProps。

function handleNewProps() {if (mapStateToProps.dependsOnOwnProps)stateProps = mapStateToProps(state, ownProps)if (mapDispatchToProps.dependsOnOwnProps)dispatchProps = mapDispatchToProps(dispatch, ownProps)mergedProps = mergeProps(stateProps, dispatchProps, ownProps)return mergedProps
}

如果只有state改变的话:

function handleNewState() {const nextStateProps = mapStateToProps(state, ownProps)const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps)stateProps = nextStatePropsif (statePropsChanged)mergedProps = mergeProps(stateProps, dispatchProps, ownProps)return mergedProps
}

主要看这里,这里就是处理store中state的改变引起的更新,这里会重新计算stateProps,然后会去严格比较,如果改变了则会重新计算mergedProps,如果没有变则会将旧的返回出去,也就是说外面对比即使是严格对比也会是相等的,也就不会引起组件更新。

这个实现结构层次分明,逻辑清晰。首先看设置了什么模式,pure还是非pure。如果是pure实现中还分为第一次执行还是非第一次执行,如果是第一次则收集数据,非第一次需要对比数据。还清晰的将真正传给业务组件的props分为了三个类型,一个是来自state的一个是来自dispatch的还有一个来自父组件传给业务子组件的。将这三方合并在一起传递给业务子组件。

而其中做的优化还有很多,例如props改变的时候,如果mapStateToProps和mapDispatchToProps不依赖props则不会重新去计算。

整个设计也是依赖注入的一个例子,顶层函数finalPropsSelectorFactory中需要用的元素都是通过参数注入进来的,需要找的话需要往上两级才能找到来源。

到这里pure的两层优化父组件render引起子组件不必要的更新,通过React.memo来阻止,而不相关的store中state的更新则被函数handleNewState阻止,如果发现没变则返回老的mergedProps。

  • 阅读react-redux源码 - 零
  • 阅读react-redux源码 - 一
  • 阅读react-redux源码(二) - createConnect、match函数的实现
  • 阅读react-redux源码(三) - mapStateToPropsFactories、mapDispatchToPropsFactories和mergePropsFactories
  • 阅读react-redux源码(四) - connectAdvanced、wrapWithConnect、ConnectFunction和checkForUpdates
  • 阅读react-redux源码(五) - connectAdvanced中store改变的事件转发、ref的处理和pure模式的处理
  • 阅读react-redux源码(六) - selectorFactory处理store更新

阅读react-redux源码(六) - selectorFactory处理store更新相关推荐

  1. 逐行阅读redux源码(二)combineReducers

    前情提要 逐行阅读redux源码(一)createStore 认识reducers 在我们开始学习源码之前,我们不妨先来看看何谓reducers: 如图所见,我们可以明白, reducer 是用来对初 ...

  2. [react] 你阅读了几遍React的源码?都有哪些收获?你是怎么阅读的?

    [react] 你阅读了几遍React的源码?都有哪些收获?你是怎么阅读的? 0遍 +1 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚持一定很酷.欢迎大家一起讨论 主目录 与歌 ...

  3. 阅读react-redux源码(五) - connectAdvanced中store改变的事件转发、ref的处理和pure模式的处理

    阅读react-redux源码 - 零 阅读react-redux源码 - 一 阅读react-redux源码(二) - createConnect.match函数的实现 阅读react-redux源 ...

  4. 学习 redux 源码整体架构,深入理解 redux 及其中间件原理

    如果觉得内容不错,可以设为星标置顶我的公众号 1. 前言 你好,我是若川.这是学习源码整体架构系列第八篇.整体架构这词语好像有点大,姑且就算是源码整体结构吧,主要就是学习是代码整体结构,不深究其他不是 ...

  5. Redux 源码解读 —— 从源码开始学 Redux

    已经快一年没有碰过 React 全家桶了,最近换了个项目组要用到 React 技术栈,所以最近又复习了一下:捡起旧知识的同时又有了一些新的收获,在这里作文以记之. 在阅读文章之前,最好已经知道如何使用 ...

  6. 中秋福利 | 漂亮的React后台源码真情大放送

    中秋快乐 每逢佳节倍思亲,一年一度的中秋,你和谁一起度过?如果你和小编一样,漂泊在外,别忘记给远在家乡的父母打个电话,祝福他们中秋快乐,告诉他们自己还好,勿让他们挂念.在此小编,祝各位粉丝们" ...

  7. 读redux源码总结

    redux介绍 redux给我们暴露了这几个方法 {createStore,combineReducers,bindActionCreators,applyMiddleware,compose } 我 ...

  8. 深入理解redux之从redux源码到react-redux的原理

    在使用react的过程中,用redux来管理应用中的状态,使应用流更清晰的同时也会有小小的疑惑,比如reducer在redux中时怎么发挥作用的,为什么只要写好reducer,和dispatch特定a ...

  9. Android --- IjkPlayer 阅读native层源码之解码成功后的音频数据如何发送回Android播放(九)

    整章目录:Android------- IjkPlayer 源码学习目录 本篇会有很多源代码,请注意阅读每行代码上面的注释. 本篇介绍的主要内容为上图红框圈起部分: 在前面介绍了如何将一个AvPack ...

最新文章

  1. 可动态扩展的数据库模型设计
  2. 百度 Serverless 函数计算引擎 EasyFaaS 正式开源
  3. 计算机信息处理教案,冀教版七年级信息技术第二课计算机--信息处理工具 教案...
  4. SVN更新时报403错误
  5. 简析EDMX文件的构成 - CSDL、SSDL、MSL
  6. 常用的实现Javaweb页面跳转的方式
  7. web跨域问题解决方案
  8. Jquery.dataTables分页排序参数详解
  9. [测试模式]Setup方法的滥用
  10. 构建手机apk的过程
  11. php如何使用代码生成pdf文件,教你利用PHP生成PDF文件
  12. xlsx文件损坏修复工具_野猫软件榜之文件修复工具
  13. 【PS功能学习】10:蒙版带你领略台前幕后的故事
  14. 【网站】八大极品桌面壁纸网站,惊艳
  15. Rabbitmq关于guest用户登录失败解决方法
  16. MATLAB---构造一个插值三次样条曲线
  17. 想开发一套多人交友app必须知道的
  18. php强类型 vscode,VSCode 扩展入门,后缀代码补全的实现
  19. 用matlab作乌鸦喝水,仿真程序动画作品--乌鸦喝水
  20. Shell脚本读取mysql结果集各数据项的值

热门文章

  1. 问题 1051: [编程入门]结构体之成绩统计2
  2. springboot 静态资源访问,和文件上传 ,以及路径问题
  3. 数据结构(六)查找---多路查找树(2-3-4树)
  4. Android深度探索(卷1)HAL与驱动开发 读书笔记(第四章)
  5. 用汇编语言写的第一个DOS程序
  6. 莫队算法 BOJ 2038 [2009国家集训队]小Z的袜子(hose)
  7. 使自定义控件居中于父容器的计算公式
  8. Mono for android,Xamarin点击事件的多种写法
  9. Quartz作业调度框架及时间表达式的含义和语法
  10. 创建和应用Java包文件的两种方式(转)