阅读react-redux源码(三) - mapStateToPropsFactories、mapDispatchToPropsFactories和mergePropsFactories

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

mapStateToPropsFactories

import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'export function whenMapStateToPropsIsFunction(mapStateToProps) {return typeof mapStateToProps === 'function'? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps'): undefined
}export function whenMapStateToPropsIsMissing(mapStateToProps) {return !mapStateToProps ? wrapMapToPropsConstant(() => ({})) : undefined
}export default [whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing]

首先mapStateToPropsFactories是一个数组[whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing]

从名字中很轻易就能看出来数组中两个函数分别处理什么任务。从右往左分别处理了,mapStateToProps参数为假值和mapStateToProps传入的是函数的情况。

whenMapStateToPropsIsMissing:如果入参是假值,那么就返回默认的函数,否则返回undefined。这个undefined被match捕获之后,match会继续遍历factories的下一项,也就是whenMapStateToPropsIsFunction。

whenMapStateToPropsIsFunction:如果mapStateToProps入参是函数,那么就返回wrapMapToPropsFunc(mapStateToProps, ‘mapStateToProps’)的返回值,否则返回undefined。也就是说入参不是函数就会返回undefined。到这里mapStateToPropsFactories数组就结束了,如果最后返回的是undefined,那么match函数会返回一个抛出错误的函数,当执行这个函数的时候回抛出错误。

所以对于入参mapStateToProps来说,如果需要可以传入一个函数,用于筛选组件需要的store.state,否则可不传,但是不能传入非function类型的真值,例如:true,string、number、object之类的值,否则会报错。

wrapMapToPropsConstant 和 wrapMapToPropsFunc

import verifyPlainObject from '../utils/verifyPlainObject'export function wrapMapToPropsConstant(getConstant) {return function initConstantSelector(dispatch, options) {const constant = getConstant(dispatch, options)function constantSelector() {return constant}constantSelector.dependsOnOwnProps = falsereturn constantSelector}
}// dependsOnOwnProps is used by createMapToPropsProxy to determine whether to pass props as args
// to the mapToProps function being wrapped. It is also used by makePurePropsSelector to determine
// whether mapToProps needs to be invoked when props have changed.
//
// A length of one signals that mapToProps does not depend on props from the parent component.
// A length of zero is assumed to mean mapToProps is getting args via arguments or ...args and
// therefore not reporting its length accurately..// function mapStateToProps(state, ownProps) { }
export function getDependsOnOwnProps(mapToProps) {return mapToProps.dependsOnOwnProps !== null &&mapToProps.dependsOnOwnProps !== undefined? Boolean(mapToProps.dependsOnOwnProps): mapToProps.length !== 1
}// Used by whenMapStateToPropsIsFunction and whenMapDispatchToPropsIsFunction,
// this function wraps mapToProps in a proxy function which does several things:
//
//  * Detects whether the mapToProps function being called depends on props, which
//    is used by selectorFactory to decide if it should reinvoke on props changes.
//
//  * On first call, handles mapToProps if returns another function, and treats that
//    new function as the true mapToProps for subsequent calls.
//
//  * On first call, verifies the first result is a plain object, in order to warn
//    the developer that their mapToProps function is not returning a valid result.
//
export function wrapMapToPropsFunc(mapToProps, methodName) {return function initProxySelector(dispatch, { displayName }) {const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {return proxy.dependsOnOwnProps? proxy.mapToProps(stateOrDispatch, ownProps): proxy.mapToProps(stateOrDispatch)}// allow detectFactoryAndVerify to get ownPropsproxy.dependsOnOwnProps = trueproxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch,ownProps) {proxy.mapToProps = mapToPropsproxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)let props = proxy(stateOrDispatch, ownProps)// function mapToStateWrap() { return function mapToState(state, ownProps) {} }if (typeof props === 'function') {proxy.mapToProps = propsproxy.dependsOnOwnProps = getDependsOnOwnProps(props)props = proxy(stateOrDispatch, ownProps)}if (process.env.NODE_ENV !== 'production')verifyPlainObject(props, displayName, methodName)return props}return proxy}
}

这两个包裹函数的目的则是为mapToProps函数增强一些功能,mapToPrps函数包括mapStateToProps和mapDispatchToProps。

wrapMapToPropsConstant:该函数会用一个返回常量的函数作为入参,并返回一个函数。被返回的函数的返回值也是一个函数,也就是说一共有三层,需要执行三次才能拿到结果,也就是入参函数返回的常量。

wrapMapToPropsFunc:这个函数比较复杂。它返回一个mapToProps的代理函数,代理函数增加了几个功能点:

  1. 判断mapStateToProps函数是否依赖组件props
  2. 首次调用处理mapStateToProps返回一个mapStateToProps函数的情况
  3. 首次调用验证mapStateToProps返回值是一个简单对象

首先定义一个proxy函数:

const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {return proxy.dependsOnOwnProps? proxy.mapToProps(stateOrDispatch, ownProps): proxy.mapToProps(stateOrDispatch)
}

这个函数会检查是否依赖组件原来的props如果依赖则传入,否则不传入。

默认mapToProps函数依赖组件被传入的props:

proxy.dependsOnOwnProps = true

这一段比较烧脑了,定义了proxy函数中首次真正执行的proxy.mapToProps函数:

proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch,ownProps
) {proxy.mapToProps = mapToProps proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)let props = proxy(stateOrDispatch, ownProps)// function mapToStateWrap() { return function mapToState(state, ownProps) {} }if (typeof props === 'function') {proxy.mapToProps = propsproxy.dependsOnOwnProps = getDependsOnOwnProps(props)props = proxy(stateOrDispatch, ownProps)}if (process.env.NODE_ENV !== 'production')verifyPlainObject(props, displayName, methodName)return props
}

首先这个函数是stateOrDispatch,而入参是stateOrDispatch和需要连接store的组件自身的props。

将mapToProps赋值给proxy.mapToProps,然后检查mapToProps是否依赖ownProps。然后执行 :

proxy.dependsOnOwnProps? proxy.mapToProps(stateOrDispatch, ownProps): proxy.mapToProps(stateOrDispatch)

上两行代码已经将proxy.dependsOnOwnProps和proxy.mapToProps换掉了所以不会形成死循环再次进入当前方法proxy.mapToProp方法。

获取到mapToProps的返回值,检查返回值,如果是function那么这个代理函数就会将这个props当做真正的mapToProps函数。然后再赋值并且执行一遍。

if (typeof props === 'function') {proxy.mapToProps = propsproxy.dependsOnOwnProps = getDependsOnOwnProps(props)props = proxy(stateOrDispatch, ownProps)
}

再接下来则会判断新得到的props是否是简单对象。如果不是简单对象并且不在生产环境中就要报错,否则直接返回props。

小结:wrapMapToPropsFunc函数将将mapStateToProps和mapDispatchToProps归为一类,都是输入一个值,输出一个对象,过程都一样,所以具体代理的是mapDispatch还是mapState只和入参有关,代理的过程是一样的。

getDependsOnOwnProps

export function getDependsOnOwnProps(mapToProps) {return mapToProps.dependsOnOwnProps !== null &&mapToProps.dependsOnOwnProps !== undefined? Boolean(mapToProps.dependsOnOwnProps): mapToProps.length !== 1
}

这个函数相对简单,如果mapToProps有显示声明是否依赖ownProps如果有将其转换为boolean返回,否则使用隐式判断记mapToProps的入参有几个,如果不是1个(其实这里应该是大于1吧?)则认为入参至少是两个,而第二个参数就希望是ownProps。

verifyPlainObject

这个方法主要验证对象是否是简单对象,具体说只做存储数据用,具体为什么要求mapToProps函数返回的是简单对象,就并没有头绪了。 判断是否是简单对象的方法如下:

export default function isPlainObject(obj) {if (typeof obj !== 'object' || obj === null) return falselet proto = Object.getPrototypeOf(obj)if (proto === null) return truelet baseProto = protowhile (Object.getPrototypeOf(baseProto) !== null) {baseProto = Object.getPrototypeOf(baseProto)}return proto === baseProto
}

如果入参不是对象则不是简单对象,因为连对象都不是。

获取对象原型,如果对象原型是null(Object.create(null))则是简单对象。

遍历原型链,找到原型上倒数第二个节点,与proto对于,如果相等则表示obj是简单对象,否则不是简单对象。

class A extends B {} A的实例就不是简单对象,因为A的原型是B.prototype而不是Object.prototype。

未解决的问题:为什么要遍历而不是检查两层原型链?如:

export default function isPlainObject(obj) {if (typeof obj !== 'object' || obj === null) return falselet proto = Object.getPrototypeOf(obj)if (proto === null) return truereturn Object.getPrototypeOf(proto) === null
}

mapDispatchToPropsFactories

import { bindActionCreators } from 'redux'
import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'export function whenMapDispatchToPropsIsFunction(mapDispatchToProps) {return typeof mapDispatchToProps === 'function'? wrapMapToPropsFunc(mapDispatchToProps, 'mapDispatchToProps'): undefined
}export function whenMapDispatchToPropsIsMissing(mapDispatchToProps) {return !mapDispatchToProps? wrapMapToPropsConstant(dispatch => ({ dispatch })): undefined
}export function whenMapDispatchToPropsIsObject(mapDispatchToProps) {return mapDispatchToProps && typeof mapDispatchToProps === 'object'? wrapMapToPropsConstant(dispatch =>bindActionCreators(mapDispatchToProps, dispatch)): undefined
}export default [whenMapDispatchToPropsIsFunction,whenMapDispatchToPropsIsMissing,whenMapDispatchToPropsIsObject
]

Factories从右往左mapDispatchToProps分别是object的情况,没有传的情况和是函数的情况。

其中使用的到的wrapMapToPropsConstantwrapMapToPropsFunc在上面有介绍,而bindActionCreators的入参希望是一个对象,对象对应的值是actionCreator,执行的结果则是将actionCreator用函数包起来,例如这样:

function bindActionCreator(actionCreator, dispatch) {return function() {return dispatch(actionCreator.apply(this, arguments))}
}

mergePropsFactories

import verifyPlainObject from '../utils/verifyPlainObject'export function defaultMergeProps(stateProps, dispatchProps, ownProps) {return { ...ownProps, ...stateProps, ...dispatchProps }
}export function wrapMergePropsFunc(mergeProps) {return function initMergePropsProxy(dispatch,{ displayName, pure, areMergedPropsEqual }) {let hasRunOnce = falselet mergedPropsreturn function mergePropsProxy(stateProps, dispatchProps, ownProps) {const nextMergedProps = mergeProps(stateProps, dispatchProps, ownProps)if (hasRunOnce) {if (!pure || !areMergedPropsEqual(nextMergedProps, mergedProps))mergedProps = nextMergedProps} else {hasRunOnce = truemergedProps = nextMergedPropsif (process.env.NODE_ENV !== 'production')verifyPlainObject(mergedProps, displayName, 'mergeProps')}return mergedProps}}
}export function whenMergePropsIsFunction(mergeProps) {return typeof mergeProps === 'function'? wrapMergePropsFunc(mergeProps): undefined
}export function whenMergePropsIsOmitted(mergeProps) {return !mergeProps ? () => defaultMergeProps : undefined
}export default [whenMergePropsIsFunction, whenMergePropsIsOmitted]

和上面两个factories一样的模式,主要看下函数wrapMergePropsFunc主要做的事情是缓存mergedProps,如果对比出

nextMergedProps计算出来的props和缓存的mergedProps一样,那么就直接返回缓存的mergedProps。其中最终的props主要有三个来源,一个是stateProps,一个是dispatchProps还有一个是ownProps。

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

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

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

相关文章

Xcode 升级后,常常遇到的遇到的警告、错误,解决方法(转)

从sdk3.2.5升级到sdk 7.1中间废弃了很多的方法,还有一些逻辑关系更加严谨了。1,警告:“xoxoxoxo” is deprecated解决办法:查看xoxoxoxo的这个方法的文档,替换掉这个方法即可。2,警告:Declarat…

.net 垃圾回收学习[How To: Use CLR Profiler][翻译学习]【2】

http://msdn.microsoft.com/zh-cn/library/ms979205 注意:内容可能已经过期了。 注意:CLR Profiler最新版本:http://www.microsoft.com/download/en/details.aspx?id16273 Identifying Common Garbage Collection Issues 可以使用CLR Profil…

JavaOne 2014:会议与合同利益冲突

杜克街咖啡馆,工程师可以在街上进行走廊交谈 。 与签约不兼容 我的第11届JavaOne会议(2004年至2014年为11 10 1)非常出色。 值得参加此活动并结识社区中所有参与的人。 现在,这里是绅士的,但 。 除了经济上的明显优…

JQuery(三)-- AJAX的深入理解以及JQuery的使用

HTTP HTTP http: 超文本传输协议。特点: 简单、快速、灵活、无状态、无连接 URL: 统一资源定位符。 组成:协议名://主机IP:端口号/项目资源地址?传递参数的键值对#锚点 ①ip地址在同一个网段是唯一的。如果是在公…

阅读react-redux源码(四) - connectAdvanced、wrapWithConnect、ConnectFunction和checkForUpdates

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

(转)模拟鼠标/键盘

鼠标操作类 using System;namespace Edobnet.Net.Lib{/// <summary>/// Mouse 的摘要说明。/// </summary>public class Mouse{public Mouse(){//// TODO: 在此处添加构造函数逻辑//}internal const byte SM_MOUSEPRESENT 19;internal const byte SM_CMOUSEBUTTON…

c++ 返回 char*

一段在C里经常犯错误的代码 一个类&#xff1a; class C{public:C(){}~C(){}public:string a;string funa(){string tmp "1234";return tmp;}};外部调用类C并使用其成员&#xff1a; C classc;char *test1 classc.a.c_str();printf("%s\n", test1);上述正…

JSF的工作方式和调试方式–可以使用polyglot吗?

JSF不是我们通常认为的那样。 这也是一个调试起来可能有些棘手的框架&#xff0c;尤其是在初次遇到时。 在这篇文章中&#xff0c;让我们继续探讨为什么会出现这种情况&#xff0c;并提供一些JSF调试技术。 我们将讨论以下主题&#xff1a; JSF不是我们经常想到的 JSF调试的难…

React组件实现越级传递属性

如果有这样一个结构&#xff1a;三级嵌套&#xff0c;分别是&#xff1a;一级父组件、二级子组件、三级孙子组件&#xff0c;且前者包含后者&#xff0c;结构如图&#xff1a; 如果把一个属性&#xff0c;比如color&#xff0c;从一级传递给三级&#xff0c;一般做法是使用prop…

尝试Office 2003 VSTO的开发、部署

背景&#xff1a;一年前&#xff0c;某项目需要使用到Excel进行数据录入&#xff0c;考虑到很多用户还是使用XPOffice 2003&#xff0c;所以开发的时候直接使用Excel 2003版本进行VBA开发。也许很多人都会说&#xff0c;Win10都出了&#xff0c;微软的Office都要免费了&#xf…

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

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

Servlet编程API

一、基本的servlet APIJavaEE关于Servlet的API主要有两个包&#xff1a;javax.servlet和javax.servlet.http。前者主要提供了Web容器能够使用的servlet基本类和接口&#xff0c;后者主要包括和HTTP协议相关的servlet类和接口。对servlet的编程&#xff0c;主要是根据需要&#…

初级开发人员在编写单元测试时常犯的错误

自从我编写第一个单元测试以来已经有10年了。 从那时起&#xff0c;我不记得我已经编写了成千上万的单元测试。 老实说&#xff0c;我在源代码和测试代码之间没有任何区别。 对我来说是同一回事。 测试代码是源代码的一部分。 在过去的3-4年中&#xff0c;我与多个开发团队合作…

OpenDaylight开发hello-world项目之开发工具安装

OpenDaylight开发hello-world项目之开发环境搭建 OpenDaylight开发hello-world项目之开发工具安装 OpenDaylight开发hello-world项目之代码框架搭建 在ODL开发之前&#xff0c;要安装好开发环境。ODL使用java语言开发&#xff0c;所以要安装好java。ODL的代码框架是有maven这个…

Google Chrome 扩展程序开发

根据公司的规定&#xff0c;每月八小时&#xff0c;弹性工作制。所以大家平时来的不太准时&#xff0c;如果有事&#xff0c;下班也就早些回去了。所以一个月下来工作时间可能不够&#xff0c;但是公司的考勤日历是这样的&#xff1a; 除了请假和法定节假日外&#xff0c;其他样…

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

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

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

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

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

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

window.Event参数详解

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

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

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