react-redux源码解析
react-redux作为大型react应用中链接react和redux的链接库,在很多reat项目中广泛应用。作为开发者的同学们了解reac-redux的原理和源码对我们的能力提升具有一定的帮助,所谓知其然知其所以然。
预备知识
(建议不知道connectAdvanced API,connectd的四个参数不是特别熟的同学先去简单过一下)
准备工作
老规矩github 找到 , clone master
本文版本: 6.0.0-beta.3复制代码
当然你也可以选择看笔者的, 其中包含redux和react-redux的源码及详细的注释
开始撸码
打开项目之后,其他的不多说了,我们直接看src里面的核心代码,从index.js 开始(哦, 这里插一嘴,react-redux是基于react和redux实现的,可以看一下package.json里面的peerDependencies)
一如既往的简单, 暴露出的Provider, connectAdvanced, connect 都有对应的api, 我们先来看看Provider的实现。
Provider实现
我们先想一想reac-redux都干了什么,简单来说用 < Provider >< /Provider>包装了根组件, 把 store 作为 props 传递到每一个被 connect() 包装的组件,其中state to props的计算过程做了优化。 react本身倡导props的自上而下单项传递,但是也提供context支持跨层级传递,到这里,聪明的你是不是联想到了provider与context有关呢,直接看?源码相关注释以添加
建议先看Provider.propTypes提供了那些props -> constructor -> render -> 声明周期钩子方法 -> 总结
import React, { Component } from 'react'import PropTypes from 'prop-types'import { ReactReduxContext } from './Context'class Provider extends Component { constructor(props) { super(props) // 获取store const { store } = props // 初始化state, storeState为初始的redux state this.state = { storeState: store.getState(), // 保存init store store } } componentDidMount() { // 文件收索_isMounted, 共三处, componentWillUnmount中赋值为false // 先假设为标记componentDidMount -> componentWillUnmount中 this._isMounted = true // 来看下subscribe this.subscribe() } componentWillUnmount() { // 取消监听subscribe if (this.unsubscribe) this.unsubscribe() // 生命周期结束this._isMounted 赋值为false =>_isMounted假设成立✅ this._isMounted = false } componentDidUpdate(prevProps) { // 如果更新之后,store的引用发生变化 if (this.props.store !== prevProps.store) { // 如果存在监听则取消 if (this.unsubscribe) this.unsubscribe() // 更新storeState this.subscribe() } /* * 在我的理解中redux的store应该是不变的,主要就是为了提供"全局变量state" * 既然redux作者这样写,我们来思考一下为什么 * 一般store应该是这样 let store = createStore(rootReducer, initState, applyMiddleware(xxx)); * this.props.store !== prevProps.store说明全局的store发生了变化。既createStore方法的重新调用, * 阅读过redux源码的同学应该知道, 既然是新的createStore, "全局的state"会发生变化,不再是之前的内存空间 * 所有这里再次调用subscribe更新state里面的storeState * * * */ } // 使用store.subscribe方法,保证storeState的最新 subscribe() { const { store } = this.props // 监听subscribe this.unsubscribe = store.subscribe(() => { // 获取最新的state赋值给newStoreState const newStoreState = store.getState() // 不在本次生命周期中return if (!this._isMounted) { return } this.setState(providerState => { // If the value is the same, skip the unnecessary state update. // 如果state是相同的引用, 直接跳过state的更新 if (providerState.storeState === newStoreState) { return null } // 更新当前storeState return { storeState: newStoreState } }) }) const postMountStoreState = store.getState() if (postMountStoreState !== this.state.storeState) { this.setState({ storeState: postMountStoreState }) } } render() { // ReactReduxContext为默认context, 点过去看一下默认值。 看 -> context.js文件,createContext参数是null const Context = this.props.context || ReactReduxContext // value 为this.state return ({this.props.children} ) }}Provider.propTypes = { // 接受store做为props, 并规定store object中的必要方法,既redux createStore的返回对象 store: PropTypes.shape({ subscribe: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired, getState: PropTypes.func.isRequired }), // 接收自定义context context: PropTypes.object, // children, 通常为根容器children: PropTypes.any}export default Provider/** * provider总结 * * provider是react-redux提供是react应用的入口组件, 一般为顶层组件 * 使用react的context传递store, 老版本的provider用的getChildContext方法, 随着react context的api改变,生产者消费者模式的新api尤然而生,因此 * provider的源码已经重构, 提供的context为{ store, storeState: state},state保存为最新 * * * */复制代码
Context.js 提供全局的初始化ReactReduxContext
import React from 'react'// defaultValue 为 nullexport const ReactReduxContext = React.createContext(null)// export出来,以便提供给provider和consumer使用export default ReactReduxContext复制代码
connect 实现
先回顾一下connect的使用形式:
connect([mapStateToProps],[mapDispatchToProps],[mergeProps],[options])()复制代码
这里还要了解一下connectAdvanced(selectorFactory, [connectOptions])方法,它是一个将 React 组件连接到 Redux store 的函数。这个函数是 connect() 的基础。
由于connect方法内引用文件,function及function的嵌套比较多,如果看官老爷不想过于在意细节,可以直接看一下
开始看connect源码 -> connect.js,简单看看结构和引用了哪些东西 ->然后我们从export default开始->遇到调用的function再去具体看实现了哪些功能
connect.js
/** * 先回顾一下connect的参数connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]) * 作用:连接 React 组件与 Redux store * mapDispatchToProps(dispatch, [ownProps]) * * react-redux 暴露的api, connectAdvanced(selectorFactory, [connectOptions]) * 连接react组件和redux的store, 这个参数是connect的基础*/import connectAdvanced from '../components/connectAdvanced'import shallowEqual from '../utils/shallowEqual'import defaultMapDispatchToPropsFactories from './mapDispatchToProps'// mapStateToProps(state, ownProps)// 只要 Redux store 发生改变,mapStateToProps 函数就会被调用, // 或者如果有ownProps参数组件接收到新的props,mapStateToProps同样会被调用import defaultMapStateToPropsFactories from './mapStateToProps'// mapStateToProps() 与 mapDispatchToProps() 的执行结果和组件自身的 props 将传入到这个回调函数中import defaultMergePropsFactories from './mergeProps'// 定制 connector 如 pure = true 等等等...import defaultSelectorFactory from './selectorFactory'/** * * 这里先以mapStatetoProps为例子, 其他的同理,简单了解后续用到再看 * @param {*} arg 使用时传进来的mapStatetoProps * @param {*} factories array[function, function] * 默认工厂defaultMapStateToPropsFactories, [whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing] * @param {*} name string * @returns function */function match(arg, factories, name) { // 后到前遍历factories for (let i = factories.length - 1; i >= 0; i--) { // 调用factories, 返回值赋值给result, 去看 -> mapStateToProps.js const result = factories[i](arg) // result为true返回result, result为(dispatch, options) => function(){...}的function if (result) return result } // 不符合connect方法规则throw Error return (dispatch, options) => { throw new Error( `Invalid value of type ${typeof arg} for ${name} argument when connecting component ${ options.wrappedComponentName }.` ) }}// 判断对象的索引是否相同function strictEqual(a, b) { return a === b}// 暴露createConnect, 想必肯定会返回function因为connect是个functionexport function createConnect({ // 一些带有默认值的参数, 我们看下面, 具体用到了在看? connectHOC = connectAdvanced, // connectAdvanced(selectorFactory, [connectOptions]) mapStateToPropsFactories = defaultMapStateToPropsFactories, // array[function, function] mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory} = {}) { // 返回的connect function return function connect( // connect的四个可选参数 mapStateToProps, mapDispatchToProps, mergeProps, // 配置参数 { pure = true, // 是否就行浅比较的配置 // 判断是否相同引用的function areStatesEqual = strictEqual, // shallowEqual 详情看下面? areOwnPropsEqual = shallowEqual, areStatePropsEqual = shallowEqual, areMergedPropsEqual = shallowEqual, // 其他配置项 ...extraOptions } = {} ) { // mapStateToProps初始化 const initMapStateToProps = match( // 使用时传递的mapStateToProps function mapStateToProps, // 默认值 -> 先看match方法, 然后我们来看mapStateToProps.js mapStateToPropsFactories, 'mapStateToProps' ) // mapDispatchToProps初始化 const initMapDispatchToProps = match( mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps' ) // mergeProps的初始化 const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps') // return connectHOC function,将 React 组件连接到 Redux store 的函数 // 先来看看他的参数 // selectorFactory函数返回一个selector函数,根据store state, 展示型组件props,和dispatch计算得到新props,最后注入容器组件 // selectorFactory -> defaultSelectorFactory // 其实很熟悉react-redux api的同学应该很熟悉connectHOC的参数, 因为他就是connectAdvanced方法啊, 建议先看看api return connectHOC(selectorFactory, { // 用于错位信息 methodName: 'connect', // 用Connect包装getDisplayName getDisplayName: name => `Connect(${name})`, // mapStateToProps是否为undefined,shouldHandleStateChanges为false则不监听store state shouldHandleStateChanges: Boolean(mapStateToProps), // selectorFactory需要的几个参数 initMapStateToProps, // (dispatch, options) => initProxySelector(dispatch, { displayName }){...} initMapDispatchToProps, initMergeProps, pure, // 默认true // strictEqual, 这里很容易想到用于判断this.state是不是都一份引用 areStatesEqual, // shallowEqual浅比较 // 插个题外话,熟悉react PureComponent的同学应该可以快速反应过来!shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState) // 不熟悉的同学看过来 -> shallowEqual.js areOwnPropsEqual, areStatePropsEqual, areMergedPropsEqual, // 容错处理, 其他的配置项 ...extraOptions }) }}// connect方法 直接调用createConnectexport default createConnect()复制代码
mapStateToProps.js
import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'// 当mapStateToProps为function时调用wrapMapToPropsFuncexport function whenMapStateToPropsIsFunction(mapStateToProps) { return typeof mapStateToProps === 'function' // 看wrapMapToPropsFunc -> wrapMapToProps.js ? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps') : undefined}// match中的数组倒着遍历先看这里// 容错处理, 判断是否传递mapStateToProps参数export function whenMapStateToPropsIsMissing(mapStateToProps) { // 如果传递了mapStateToProps参数且!mapStateToProps = true返回undefined,此时result为undefined,无效调用 // 没有传递mapStateToProps参数或mapStateToProps=false -> wrapMapToPropsConstant return !mapStateToProps ? wrapMapToPropsConstant(() => ({})) : undefined}// export default是array,元素为functionexport default [whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing]/** * 小结: * * mapstateToprops参数可执行时调用wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps') * 不传递mapstateToprops参数时 wrapMapToPropsConstant(() => ({})) * 我们接着刨根问底 看->wrapMapToProps.js * * * */复制代码
wrapMapToProps.js
import verifyPlainObject from '../utils/verifyPlainObject'/** * * @export mapstateToprops为undefined调用 * @param {*} getConstant () => ({}) * @returns function initConstantSelector(dispatch, options) */export function wrapMapToPropsConstant(getConstant) { // 返回 initConstantSelector return function initConstantSelector(dispatch, options) { const constant = getConstant(dispatch, options) function constantSelector() { return constant } constantSelector.dependsOnOwnProps = false return constantSelector }}// 用来判断是否存在ownProps // mapStateToProps(state, [ownProps])export function getDependsOnOwnProps(mapToProps) { // 不是第一次调用直接返回Boolean return mapToProps.dependsOnOwnProps !== null && mapToProps.dependsOnOwnProps !== undefined ? Boolean(mapToProps.dependsOnOwnProps) // 第一次调用时mapToProps的dependsOnOwnProps为undefined,直接判断参数个数 : mapToProps.length !== 1 }/** * @export mapstateToprops传递时调用 * @param {*} mapToProps 使用connect是传递的mapStateToProprs * @param {*} methodName 名称 methodName = 'mapStatetoProps' || 'mapDispatchToProps' * @returns 返回initProxySelector(dispatch, { displayName }) */export function wrapMapToPropsFunc(mapToProps, methodName) { // 终于找到你!! 返回initProxySelector function, 这个返回值会赋值给initMapStateToProps(当然还有initDispatchToProps) return function initProxySelector(dispatch, { displayName }) { // 定义proxy function,且作为返回值 const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) { return proxy.dependsOnOwnProps // mapStateToProps计算是否依赖组件的props ? proxy.mapToProps(stateOrDispatch, ownProps) // 返回proxy.mapToProps,继续看一下他是什么鬼? : proxy.mapToProps(stateOrDispatch) } // dependsOnOwnProps标记运行依赖组件的props为true proxy.dependsOnOwnProps = true // detectFactoryAndVerify为返回的function // 梳理一下,目前调用链是这样的 // const initMapStateToProps = initProxySelector(dispatch, { displayName })=> // mapToPropsProxy(stateOrDispatch, ownProps) => detectFactoryAndVerify(stateOrDispatch, ownProps) // detectFactoryAndVerify赋值给proxy.mapToProps // 第一次调用 mapToPropsProxy时返回detectFactoryAndVerify(stateOrDispatch, ownProps) proxy.mapToProps = function detectFactoryAndVerify( stateOrDispatch, ownProps ) { // 调用的时候 mapToPropsfunction 赋值给 proxy.mapToProps // 也就是第一次除了调用到proxy.mapToProps之后, 以后在调用到proxy.mapToProps的时候则使用传递的mapToProps function proxy.mapToProps = mapToProps // 重新判断 dependsOnOwnProps(第一次默认true) proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps) // 定义props为proxy(stateOrDispatch, ownProps) // 先看下执行顺序 第一次调用initProxySelector() => proxy() => // 此时 proxy.mapToProps = detectFactoryAndVerify() // 再次调用 proxy(stateOrDispatch, ownProps)时 返回值为传递的mapToProps(...args),也就是我们react组件需要的props let props = proxy(stateOrDispatch, ownProps) // 如果props为function再次执行 if (typeof props === 'function') { proxy.mapToProps = props proxy.dependsOnOwnProps = getDependsOnOwnProps(props) props = proxy(stateOrDispatch, ownProps) } // 非production环境检查 if (process.env.NODE_ENV !== 'production') // verifyPlainObject是utils方法, 如果不是纯对象,抛出warning verifyPlainObject(props, displayName, methodName) // 返回最终的props return props } return proxy }}复制代码
mergeProps.js和mapDisptchToPtops.js同理,返回一个array[function,function],对参数做容错处理,返回一个initProxySelector function() => proxy => props, 具体细节后面介绍
shallowEqual
shallowEqual通常用来对object做浅比较,经常会出现在react应用中, 配合shouldComponentUpdate做性能优化, 如果熟悉PureComponent原理同学,那应该知道这段代码
!shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState)复制代码
shallowEqual.js
// 对象自身属性中是否具有指定的属性const hasOwn = Object.prototype.hasOwnProperty// 判断两个值是否是相同的值function is(x, y) { // SameValue algorithm if (x === y) { return x !== 0 || y !== 0 || 1 / x === 1 / y; } else { return x !== x && y !== y; }}// 浅比较 只会比较到两个对象的 ownProperty 是否符合 Object.is 判等,不会递归地去深层比较// shallowEqual({x:{}},{x:{}}) // false// shallowEqual({x:1},{x:1}) // trueexport default function shallowEqual(objA, objB) { // 相同值直接返回true,shallowEqual返回值为boolean if (is(objA, objB)) return true // 在objA和objB不是相同值的前提下, 如果objA,objB为null或非object可以判定返回false if ( typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null ) { return false } // 定义objA,objB的key数组 const keysA = Object.keys(objA) const keysB = Object.keys(objB) // length不同说明object不同 if (keysA.length !== keysB.length) return false // 循环遍历keysA for (let i = 0; i < keysA.length; i++) { // 如果objB不含有objA的key且objA与objB的value不同, 返回false if (!hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) { return false } } // 如果通过了for循环, 说明objA, objB中的第一层key和value都相同,恭喜通过 return true}复制代码
connect总结
由于connect函数设计的方法过多,我们在这里简化一下connect的代码(以下为源码的精简伪代码)
// 首先为以参数的形式为connect注入一些方法export function createConnect({ ... // connectAdvanced为react-redux暴露出的api connectHOC = connectAdvanced, ...} = {}) { // connect方法 return function connect( // 接受的四个参数 mapStateToProps, mapDispatchToProps, mergeProps, { pure = true, // 是否就行浅比较的配置 strictEqual, // 判断object引用, strictEqual(a, b)=> a === b shallowEqual, // 浅比较,上面介绍了 ...extraOptions // 其他配置项 } = {} ) { // 一系列的方法执行,对三个参数的类型做了容错处理 // 分别初始化了各自的参数mapStateToProps,mapDispatchToProps,mergeProps,注入了一些内部的默认参数和方法 // 他们大致是这样的function: // (dispatch, options) => initProxySelector() => mapToPropsProxy() => props const initMapStateToProps = match(...args) const initMapDispatchToProps = match(...args) const initMergeProps = match(...args) // 返回值由执行connectAdvanced获取,并传入初始化的initMapStateToProps等参数和pure等配置项 return connectAdvanced(selectorFactory, { initMapStateToProps, initMapDispatchToProps, initMergeProps, pure, ...extraOptions }) }} // 直接执行createConnect方法返回connectexport default createConnect()复制代码
connectAdvanced高阶函数
从connect()(<A/ >)中不难看出connect的返回值为一个高阶组件,包装当前的react组件,返回一个增强props的组件,在执行connect时会执行connectAdvanced,接下来我们不得不看下connectAdvanced方法的实现,先介绍一下connectAdvanced -> ?看注释
connectAdvanced.js
// hoist-non-react-statics组件,这个组件会自动把所有绑定在对象上的非React静态(static)方法都绑定到新的对象上import hoistStatics from 'hoist-non-react-statics'// 提示信息插件import invariant from 'invariant'import React, { Component, PureComponent } from 'react'import { isValidElementType } from 'react-is'// contextimport { ReactReduxContext } from './Context'/** * * @export 介绍下connectAdvanced * connect() 的基础,真正将 React 组件连接到 Redux store 的函数 * @param {*} selectorFactory function selectorFactory(dispatch, factoryOptions) * 初始化选择器函数,该选择器函数是在 connector 组件需要重新计算一个新的 props 时调用 * 作为 store 的 state 改变或者接收到一个新的 props 的结果。selector 的结果应该是一个普通对象, * 作为被包裹的组件的 props 传递。如果连续调用 selector 都返回与上一次调用相同的对象(===), * 则不会重新渲染该组件。selector 的责任是在适当的时候返回以前的对象。 * @param {*} [{ * getDisplayName = name => `ConnectAdvanced(${name})`, DisplayName * methodName = 'connectAdvanced', * renderCountProp = undefined, * shouldHandleStateChanges = true, * storeKey = 'store', * withRef = false, * context = ReactReduxContext, * ...connectOptions * }={}] * @returns */export default function connectAdvanced( // function, 使用的时候在介绍selectorFactory.js selectorFactory, // options object: { // 被包裹的组件的 DisplayName 属性 getDisplayName = name => `ConnectAdvanced(${name})`, // 用于显示错误消息 methodName = 'connectAdvanced', // 组件是否订阅 redux store 的 state 更改 shouldHandleStateChanges = true, renderCountProp = undefined, // 传递给内部组件的props键,表示render方法调用次数 // 可以获取 store 的 props/context key storeKey = 'store', // 如果为 true,则将一个引用存储到被包裹的组件实例中,并通过 getWrappedInstance() 方法使其可用 withRef = false, // 看到这个变量我的第一反应是ref的转发, 不了解的同学去看一下React.forwardRef() forwardRef = false, // provider的ReactReduxContext方法 context = ReactReduxContext, ...connectOptions } = {}) { // invariant 一个只在development环境的error // When process.env.NODE_ENV is not production, the message is required. // 这几个配置参数别使用的时候会有warnning,直接跳过这部分 invariant( renderCountProp === undefined, `renderCountProp is removed. render counting is built into the latest React dev tools profiling extension` ) invariant( !withRef, 'withRef is removed. To access the wrapped instance, use a ref on the connected component' ) const customStoreWarningMessage = 'To use a custom Redux store for specific components, create a custom React context with ' + "React.createContext(), and pass the context object to React-Redux's Provider and specific components" + ' like:. ' + 'You may also pass a {context : MyContext} option to connect' invariant( storeKey === 'store', 'storeKey has been removed and does not do anything. ' + customStoreWarningMessage ) // 定义Context const Context = context // 返回react高阶组件, WrappedComponent既包装的react组件 return function wrapWithConnect(WrappedComponent) { // 参数检验 if (process.env.NODE_ENV !== 'production') { invariant( isValidElementType(WrappedComponent), `You must pass a component to the function returned by ` + `${methodName}. Instead received ${JSON.stringify(WrappedComponent)}` ) } // 组件的displayName,默认Component const wrappedComponentName = WrappedComponent.displayName || WrappedComponent.name || 'Component' // 拼接下displayName const displayName = getDisplayName(wrappedComponentName) // 定义selectorFactoryOptions对象,包含了connect和connectAdvanced的所有参数 const selectorFactoryOptions = { // connectOptions为initMapStateToProps,initMapDispatchToProps,pure等参数 ...connectOptions, getDisplayName, methodName, renderCountProp, shouldHandleStateChanges, storeKey, displayName, wrappedComponentName, WrappedComponent } // pure决定shouldComponentUpdate是否进行shwoEqual const { pure } = connectOptions // Component赋值给OuterBaseComponent, 用react高阶的继承 let OuterBaseComponent = Component // 定义FinalWrappedComponent let FinalWrappedComponent = WrappedComponent if (pure) { // 为true用PureComponent OuterBaseComponent = PureComponent } // 接下来直接看 class Connect extends OuterBaseComponent// function makeDerivedPropsSelector() { // 定义变量, 用来保存上一次makeDerivedPropsSelector中的值 // 变量语义化我们不难猜出意义 let lastProps let lastState let lastDerivedProps let lastStore let sourceSelector return function selectDerivedProps(state, props, store) { // props为父组件的props // pure为true时,props和state的引用都没有变化, 直接返回lastDerivedProps(第一次肯定不会成立) // 这里不难理解, 就是要求你用"纯"的state和props if (pure && lastProps === props && lastState === state) { return lastDerivedProps } if (store !== lastStore) { // store赋值lastStore, 更新lastStore // 除第一次调用外一般不会 lastStore = store // selectorFactory为connect传入的function,默认值来自selsctorFactory.js的export default sourceSelector = selectorFactory( store.dispatch, selectorFactoryOptions // 所有参数集合 ) } // props赋值给lastProps lastProps = props // state赋值给lastState lastState = state // 调用sourceSelector参入redux state和props, 得到最新的props // 不难看出selectorFactory的返回值为一个function, 目前我们可以猜测到 // selsctorFactory.js的export default function 大体结构是这样(dispatch, selectorFactoryOptions)=>(state, props) => newProps const nextProps = sourceSelector(state, props) // 新旧props引用相同 if (lastDerivedProps === nextProps) { // 直接返回 return lastDerivedProps } // 新旧props引用不相同, nextProps赋值给lastDerivedProps lastDerivedProps = nextProps // 返回lastDerivedProps return lastDerivedProps // 最后我们去看selsctorFactory.js到底如何合并的state和props } } function makeChildElementSelector() { // 定义props, ref, element变量 let lastChildProps, lastForwardRef, lastChildElement // 返回function return function selectChildElement(childProps, forwardRef) { // 判断新旧props, hre, elelment是否相同 if (childProps !== lastChildProps || forwardRef !== lastForwardRef) { // 如果不同重新赋值 lastChildProps = childProps lastForwardRef = forwardRef lastChildElement = ( // return FinalWrappedComponent, 改变props和ref ) } // react组件 return lastChildElement } } class Connect extends OuterBaseComponent { constructor(props) { super(props) invariant( forwardRef ? !props.wrapperProps[storeKey] : !props[storeKey], 'Passing redux store in props has been removed and does not do anything. ' + customStoreWarningMessage ) // 添加selectDerivedProps和selectChildElement方法 // selectDerivedProps为function是makeDerivedPropsSelector的返回值 this.selectDerivedProps = makeDerivedPropsSelector() // selectChildElement为function this.selectChildElement = makeChildElementSelector() // bind this this.renderWrappedComponent = this.renderWrappedComponent.bind(this) } // value为context,既provider中的{storeState: store.getState(),store} renderWrappedComponent(value) { invariant( value, `Could not find "store" in the context of ` + `"${displayName}". Either wrap the root component in a , ` + `or pass a custom React context provider to and the corresponding ` + `React context consumer to ${displayName} in connect options.` ) // 获取redux state和store const { storeState, store } = value // 定义wrapperProps为this.props let wrapperProps = this.props let forwardedRef // forwardRef为真时, Connect组件提供了forwardedRef = {ref} if (forwardRef) { // wrapperProps为props中的wrapperProps wrapperProps = this.props.wrapperProps // forwardedRef赋值为props的forwardedRef, 传递的是ref // 用于传递给子组件WrappedComponent既let FinalWrappedComponent = WrappedComponent中的FinalWrappedComponent forwardedRef = this.props.forwardedRef } // 导出props let derivedProps = this.selectDerivedProps( storeState, wrapperProps, store ) // 返回最终的组件,传入最终的props和ref -> 看selectChildElement发放 return this.selectChildElement(derivedProps, forwardedRef) } render() { // 默认情况下公用的ReactReduxContext const ContextToUse = this.props.context || Context return ( // 的消费者 { this.renderWrappedComponent} ) } } // 相当于插件 // 包装的组件赋值给Connect.WrappedComponent Connect.WrappedComponent = WrappedComponent // 添加displayName Connect.displayName = displayName // forwardRef为true时 if (forwardRef) { // 使用react.forwardRef为connect生成的组件的父组件提供孙子(传递给connect的组件)组件 const forwarded = React.forwardRef(function forwardConnectRef( props, ref ) { return}) // 此时connect()( )的生成组件为forwarded, 从新挂载displayName和WrappedComponent forwarded.displayName = displayName forwarded.WrappedComponent = WrappedComponent return hoistStatics(forwarded, WrappedComponent) } // 将子组件的非React的static(静态)属性或方法合并到父组件 // 返回拓展过props属性的Connect组件 return hoistStatics(Connect, WrappedComponent) }}复制代码
connectAdvanced返回一个react高阶组件, 根据pure,forwardRef配置决定是否采用PureComponent和ref的转移, 通过selectDerivedProps放法生成最终的props,传递给最终返回的react组件,最后我们去看selsctorFactory.js到底如何合并的state和props
selectorFactory
selectorFactory函数返回一个selector函数,根据store state, 展示型组件props和dispatch计算得到新props,最后注入容器组件中
selectorFactory.js实现
import verifySubselectors from './verifySubselectors'export function impureFinalPropsSelectorFactory( mapStateToProps, mapDispatchToProps, mergeProps, dispatch) { return function impureFinalPropsSelector(state, ownProps) { // 执行mergePropsProxy,返回修改后的props return mergeProps( mapStateToProps(state, ownProps), // mapStateToProps执行结果 mapDispatchToProps(dispatch, ownProps), // mapDispatchToProps的执行结果 ownProps // 自身的props ) }}export function pureFinalPropsSelectorFactory( mapStateToProps, mapDispatchToProps, mergeProps, dispatch, // areStatesEqual判断是否是相同的引用 // areOwnPropsEqual, areStatePropsEqual为shallowEqual { areStatesEqual, areOwnPropsEqual, areStatePropsEqual }) { // hasRunAtLeastOnce标记第一次执行 // 先看return的function,再看其他的function都做了什么 let hasRunAtLeastOnce = false let state let ownProps let stateProps let dispatchProps let mergedProps // 第一次执行时 function handleFirstCall(firstState, firstOwnProps) { // 直接赋值以下结果 state = firstState ownProps = firstOwnProps stateProps = mapStateToProps(state, ownProps) dispatchProps = mapDispatchToProps(dispatch, ownProps) mergedProps = mergeProps(stateProps, dispatchProps, ownProps) // hasRunAtLeastOnce标记为true hasRunAtLeastOnce = true // 返回mergedProps return mergedProps } function handleNewPropsAndNewState() { // 获取当前新的的state stateProps = mapStateToProps(state, ownProps) if (mapDispatchToProps.dependsOnOwnProps) dispatchProps = mapDispatchToProps(dispatch, ownProps) // 返回mergedProps function内部为新的object mergedProps = mergeProps(stateProps, dispatchProps, ownProps) return mergedProps } // 自身props改变 function handleNewProps() { // dependsOnOwnProps之前介绍过,判断是否有第一个参数ownprops // 如果存在需要重新执行,获取新的stateProps和mapDispatchToProps,因为自身的props改变了 if (mapStateToProps.dependsOnOwnProps) stateProps = mapStateToProps(state, ownProps) if (mapDispatchToProps.dependsOnOwnProps) dispatchProps = mapDispatchToProps(dispatch, ownProps) mergedProps = mergeProps(stateProps, dispatchProps, ownProps) return mergedProps } // redux state改变 function handleNewState() { const nextStateProps = mapStateToProps(state, ownProps) // 浅比较nextStateProps和stateProps const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps) // 更新stateProps stateProps = nextStateProps //statePropsChanged为ture,浅比较失败,mergedProps需要重新计算,mergedProps返回新对象 if (statePropsChanged) mergedProps = mergeProps(stateProps, dispatchProps, ownProps) return mergedProps } function handleSubsequentCalls(nextState, nextOwnProps) { // 执行的时候ownProps浅比较 const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps) // 比较redux state的引用 const stateChanged = !areStatesEqual(nextState, state) // nextState赋值给state state = nextState // nextOwnProps赋值给onwProps ownProps = nextOwnProps // props && state change // 看不同情况对应的return function if (propsChanged && stateChanged) return handleNewPropsAndNewState() // props change if (propsChanged) return handleNewProps() // state change if (stateChanged) return handleNewState() // propsChanged, stateChanged为true认为props,state没有改变,return mergedProps return mergedProps } return function pureFinalPropsSelector(nextState, nextOwnProps) { // state props return hasRunAtLeastOnce // 默认值为false ? handleSubsequentCalls(nextState, nextOwnProps) : handleFirstCall(nextState, nextOwnProps) }}// 找到export default/** * * * @export * @param {*} dispatch // store.dispatch * @param {*} { * initMapStateToProps // 结构initProxySelector(dispatch, { displayName }) => proxy * initMapDispatchToProps, // 结构 initMergePropsProxy(dispatch, options) => mergePropsProxy * initMergeProps, * ...options 其他配置 * } * @returns selectorFactory function */// finalPropsSelectorFactory和我们的设想结构一致export default function finalPropsSelectorFactory( dispatch, { initMapStateToProps, initMapDispatchToProps, initMergeProps, ...options }) { // 调用initProxySelector得到proxy function, proxy包含mapToProps, dependsOnOwnProps属性 const mapStateToProps = initMapStateToProps(dispatch, options) const mapDispatchToProps = initMapDispatchToProps(dispatch, options) // mergePropsProxy为function // 返回值为connect(mapstate,mapdispatch,function mergeProps(){})()中mergeProps的返回值 const mergeProps = initMergeProps(dispatch, options) // 非production环境检验 mapStateToProps,mapDispatchToProps,mergeProps if (process.env.NODE_ENV !== 'production') { verifySubselectors( mapStateToProps, mapDispatchToProps, mergeProps, options.displayName ) } // pure为true时表示selectorFactory的返回值缓存, 根据当前的redux state和ownProps的变化尽量做最出小的改变 // 详情看pureFinalPropsSelectorFactory // 否则返回新对象 const selectorFactory = options.pure ? pureFinalPropsSelectorFactory : impureFinalPropsSelectorFactory // 执行selectorFactory // selectorFactory为工厂函数,返回selector return selectorFactory( mapStateToProps, mapDispatchToProps, mergeProps, // function mergePropsProxy dispatch, options )}复制代码
mergedProps实现
import verifyPlainObject from '../utils/verifyPlainObject'export function defaultMergeProps(stateProps, dispatchProps, ownProps) { // 返回一个新对象 return { ...ownProps, ...stateProps, ...dispatchProps }}export function wrapMergePropsFunc(mergeProps) { // initMergeProps return function initMergePropsProxy( dispatch, { displayName, pure, areMergedPropsEqual } ) { // 第一次运行,设置为false let hasRunOnce = false let mergedProps return function mergePropsProxy(stateProps, dispatchProps, ownProps) { // mergeProps的返回结果 const nextMergedProps = mergeProps(stateProps, dispatchProps, ownProps) if (hasRunOnce) { // pure为fales 或者mergedProps为null if (!pure || !areMergedPropsEqual(nextMergedProps, mergedProps)) mergedProps = nextMergedProps } else { // 不是第一次运行,hasRunOnce标记为false hasRunOnce = true // nextMergedProps赋值给mergedProps mergedProps = nextMergedProps if (process.env.NODE_ENV !== 'production') verifyPlainObject(mergedProps, displayName, 'mergeProps') } // 返回修改后的props return mergedProps } }}export function whenMergePropsIsFunction(mergeProps) { // 如果mergeProps为true且是function, 调用wrapMergePropsFunc返回function initMergePropsProxy(){} return typeof mergeProps === 'function' ? wrapMergePropsFunc(mergeProps) : undefined}export function whenMergePropsIsOmitted(mergeProps) { // mergeProps !== true retun () => { ...ownProps, ...stateProps, ...dispatchProps } return !mergeProps ? () => defaultMergeProps : undefined}// 后向前执行export default [whenMergePropsIsFunction, whenMergePropsIsOmitted]/** * [mergeProps(stateProps, dispatchProps, ownProps): props] (Function): * 如果指定了这个参数,mapStateToProps() 与 mapDispatchToProps() 的执行结果和组件自身的 props 将传入到这个回调函数中。 * 该回调函数返回的对象将作为 props 传递到被包装的组件中 */复制代码