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

  • 阅读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更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361988.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Silverlight入门系列]使用MVVM模式(7):ViewModel的INotifyPropertyChanged接口实现

本文说说ViewModel的这个INotifyPropertyChanged接口可以用来做啥? 举例1:我有个TabControl,里面放了很多View,每个由ViewModel控制,我想是想TabSelectionChanged就打开相应的ViewModel,怎么做?…

无状态Spring安全性第1部分:无状态CSRF保护

如今,随着RESTful架构变得越来越标准,可能值得花一些时间重新考虑当前的安全方法。 在这个小系列的博客文章中,我们将探索一些以无状态方式解决与Web相关的安全问题的相对较新的方法。 这第一篇文章是关于保护您的网站免受跨站请求伪造&#…

window.Event参数详解

原文地址:window.Event参数详解作者:cz0090704window.evet 说明 event代表事件的状态,例如触发event对象的元素、鼠标的位置及状态、按下的键等等。 event对象只在事件发生的过程中才有效。 event的某些属性只对特定的事件有意义。比如&…

微信群运营之设计运营思路

商家要想运营好微信群,那么首要做的工作就是设计运营思路。如果做事毫无章法思路,那么很有可能会让自己的工作陷入僵局。运营微信群并不简单,需要考虑多方面社群鸭因素。卖什么产品,群管理体系的设立,规则的制定&#…

阅读react-redux源码(七) - 实现一个react-redux

阅读react-redux源码 - 零阅读react-redux源码 - 一阅读react-redux源码(二) - createConnect、match函数的实现阅读react-redux源码(三) - mapStateToPropsFactories、mapDispatchToPropsFactories和mergePropsFactories阅读react-redux源码(四) - connectAdvanced、wrapWithC…

[读书笔记]TCP/IP详解V1读书笔记-4 5

IP地址与以太网地址之间的关系 R P发送一份称作A R P请求的以太网数据帧给以太网上的每个主机。这个过程称作广播,在32 bit的I P地址和采用不同网络技术的硬件地址之间提供动态映射 ----------------------------------------- arp以太网帧的类型字段为x 0 8 0 6&am…

未来是Apache Karaf上的微服务架构

这是Jamie Goodyear的客座博客文章( 博客 , icbts )。 他是Savoir Technologies的开源倡导者,Apache开发人员和计算机系统分析师; 他为全球大型组织设计,批判和支持了体系结构。 他拥有纽芬兰纪念大学的计…

springcloud微服务多节点高性能、高可用、高并发部署

1. 共有三个服务 discovery服务,domain服务,gateway服务。 discovery服务是用来注册其他服务的,作为服务治理用。 domain服务是主业务服务。 gateway服务是所有服务的一个入口,用来做一些服务的判断和过滤用。 2. 有三台机器分别为…

只能是数字、字母、-和_

在文本框的keypress事件调用下面函数。 如 <input disabled"disabled" type"text" iduserNameToEdit οnkeypress"TextValidate()" /> 如果在文本框中按下特殊字符键&#xff0c;则显示警告信息&#xff0c;或者输入框不接受非法输入。 …

代码风格之Prettier简介

多人协作中统一的代码风格有利于项目的发展这是共识&#xff0c;但是采用什么标准来统一代码这选择就相对纷杂。项目刚开始使用了ESLint来规范代码&#xff0c;但是ESLint默认是支持JavaScript&#xff0c;加上配置可以支持TypeScript&#xff0c;而样式的支持则需要再配置Styl…

带有Swagger的Spring Rest API –集成和配置

如今&#xff0c;公开的API终于获得了应有的关注&#xff0c;公司也开始意识到其战略价值。 但是&#xff0c;使用第三方API确实是一项繁琐的工作&#xff0c;尤其是当这些API维护不当&#xff0c;设计不当或缺少任何文档时。 这就是为什么我决定四处寻找可以为集成编程人员和其…

A customized combobox with JQuery

要求实现一个轻量级的在客户端筛选的combobox&#xff0c;支持大数据量&#xff08;超过1000个items&#xff09;&#xff0c;能快速检索内容&#xff0c;并支持数据的设置和活动等基本操作。在这之前尝试过使用Jquery UI的Autocomplete&#xff0c;但是当数据量太大时客户端检…

使用内存回流的方法来实现将image的内容转换为 byte[]

在今天的开发中老大不知道怎么突发奇想&#xff0c;要使用Image的Byte数据。当时使用老几种方式效果均不理想&#xff0c;最后发现其实可以使用内存回流的方式来实现。多的不说老&#xff0c;马上贴上代码&#xff1a;/**//// <summary> /// 将byte[]转换为Image…

TypeScript中的class声明了什么

在初看TypeScript的时候在这里卡住的时间难以估计&#xff0c;并不能很好的理解”换个角度说&#xff0c;我们可以认为类具有 实例部分与 静态部分这两个部分。“这句话。今天再回头看这部分文档&#xff0c;在同事的帮助下突然有了比较通透的理解。 class Greeter {static st…

CentOS 6下搭建Apache+MySQL+PHP+SSL

网上的一些文章都已经比较老了&#xff0c;现在版本高了之后&#xff0c;其实配置是很省力的&#xff08;不考虑什么负载的话&#xff09; 分享全过程&#xff0c;出了文中提到的安装epel rpmfushion 源指令不同外&#xff0c;其他的过程也适用与Centos 5 1.安装CentOS 6 ,可以…

通过设计国际象棋游戏来了解策略模式

今天&#xff0c;我们将借助一个示例来尝试了解策略模式。 我们将考虑的示例是国际象棋游戏。 这里的目的是解释策略模式&#xff0c;而不是构建全面的国际象棋游戏解决方案。 策略模式&#xff1a;策略模式被称为行为模式-用于管理对象之间的算法&#xff0c;关系和职责。 策…

vs2010 问题 LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏

vs2010 问题 LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏 在安装 VS2010 后&#xff0c;再安装 VS2012 VS2015 等&#xff0c;原来的 .NET 4.0 会被替换为 .NET 4.5。不会恢复 .NET 4.0 。这时&#xff0c;VS2010的 cvtres.exe 就无法使用了。如果 PATH…

Nginx 使用try_files遇到的问题

背景&#xff1a; root /some/path; location / {try_files $uri $uri/ /dist/index.html; }使用React之类的的库来开发前端页面的时候&#xff0c;因为是单页应用所以需要上面的Nginx配置&#xff0c;用来在找不到html文件的时候内部重定向到/dist/index.html文件。 服务器上…

群发邮件

最近&#xff0c;通过两周的学习&#xff0c;对.net 的基础知识有了进一步的了解。觉得自己可以写个小程序了。于是花了两天时间写了一个 群发邮件的一个WinForm小程序。自己在这里小秀一下&#xff0c;表扬及鼓励一下自己。哈哈&#xff01; 此小程序在发送邮件的基础上还添加…

深入研究ES6 Generators

ES6 Generators系列&#xff1a; ES6 Generators基本概念深入研究ES6 GeneratorsES6 Generators的异步应用ES6 Generators并发 如果你还不知道什么是ES6 generators&#xff0c;请看我的前一篇文章“ES6 Generators基本概念” 。如果你已经对它有所了解&#xff0c;本文将带你…