index.js
import Provider from './components/Provider' import connectAdvanced from './components/connectAdvanced' import connect from './connect/connect' export { Provider,connectAdvanced,connect }
Provide.js
import { Component,PropTypes,Children } from 'react' // 传入Subscription的意义及用法??? import Subscription from '../utils/Subscription' import storeShape from '../utils/storeShape' import warning from '../utils/warning' // 开发环境下,组件更新时store变更,打印错误 let didWarnAboutReceivingStore = false function warnAboutReceivingStore() { if (didWarnAboutReceivingStore) { return } didWarnAboutReceivingStore = true warning( '<Provider> does not support changing `store` on the fly. ' + 'It is most likely that you see this error because you updated to ' + 'Redux 2.x and React Redux 2.x which no longer hot reload reducers ' + 'automatically. See https://github.com/reactjs/react-redux/releases/' + 'tag/v2.0.0 for the migration instructions.' ) } // Provider作为容器组件,向子组件上下文对象context注入props.store,即Redux的store export default class Provider extends Component { getChildContext() { return { store: this.store,storeSubscription: null } } constructor(props,context) { super(props,context) this.store = props.store } render() { return Children.only(this.props.children) } } if (process.env.NODE_ENV !== 'production') { Provider.prototype.componentWillReceiveProps = function (nextProps) { const { store } = this const { store: nextStore } = nextProps if (store !== nextStore) { warnAboutReceivingStore() } } } Provider.propTypes = { store: storeShape.isrequired,children: PropTypes.element.isrequired } Provider.childContextTypes = { store: storeShape.isrequired,storeSubscription: PropTypes.instanceOf(Subscription) } Provider.displayName = 'Provider'
connect.js
// connectAdvanced函数由connect模块内部调用,铰链redux.store和react组件 // 目的是通过react.props获取redux.state、redux.dispatch(action)的相关特征,传输数据流和派发事件 // 同时获得redux.dispatch特征的props.actionname派发事件时,触发react.props重新计算和组件视图重绘 // 返回函数接受react组件构造函数为参数,用以形成高阶组件,作为视图层render方法渲染的参数 import connectAdvanced from '../components/connectAdvanced' // 比较变量是否全等,或者普通对象单层比较是否相同 import shallowEqual from '../utils/shallowEqual' // 当用户配置的mapDispatchToProps是函数时,使用wrapMapToPropsFunc装饰mapDispatchToProps // 传入redux的dispatch方法、react组件的displayName // 当用户配置的mapDispatchToProps不是函数时,设置默认的mapDispatchToProps为输出redux.store.dispatch方法;否则不设置 // 接受待装饰函数,返回装饰函数构造器,执行后装饰函数 // 数组项在connect模块的match函数中挨个调用,输出为真值时即返回,增加编码的灵活性 import defaultMapDispatchToPropsFactories from './mapDispatchToProps' // 当用户配置的mapStateToProps是函数时,使用wrapMapToPropsFunc装饰mapStateToProps // 传入redux的dispatch方法、react组件的displayName // 当用户配置的mapStateToProps不是函数时,设置默认的mergeProps为空函数;否则不设置 // 接受待装饰函数,返回装饰函数构造器,执行后装饰函数 // 数组项在connect模块的match函数中挨个调用,输出为真值时即返回,增加编码的灵活性Props为空函数;否则不设置 import defaultMapStateToPropsFactories from './mapStateToProps' // 当用户配置的mergeProps是函数时,使用wrapMergePropsFunc装饰mergeProps // 传入redux的dispatch方法、react组件的displayName、pure、areMergedPropsEqual // 当用户配置的mergeProps不是函数时,设置默认的mergeProps为defaultMergeProps // 接受待装饰函数,返回装饰函数构造器,执行后装饰函数 // 数组项在connect模块的match函数中挨个调用,输出为真值时即返回,增加编码的灵活性Props为空函数;否则不设置 import defaultMergePropsFactories from './mergeProps' // 传入connectAdvanced模块 // 函数,以redux.store.dispatch、react.options为参数,返回react.props计算函数 import defaultSelectorFactory from './selectorFactory' // factories为数组形式的函数队列,用于装饰用户配置的mapStateToProps、mapDispatchToProps、mergeProps(作为参数) // 根据参数为空、为函数、为对象,按条件构造装饰函数构造器 // 该构造器接受相应redux的dispatch方法及react组件的options属性与方法 // 并传入mapStateToProps、mapDispatchToProps、mergeProps函数中,或redux方法改造mapDispatchToProps函数 // 完成装饰,最终结果是赋予react组件的props,以redux.store.state,redux.store.dispatch的方法与属性 // 针对react组件更新重绘的情形,这一过程由挂载在react组件props上的redux.store.dispatch引起 // 通过挂载在react组件上的this.selector.run重新计算props,获取redux.store.state,redux.store.dispatch // this.selector.run在react组件上挂载this.subscription实例的onStateChange方法上调用 // 而本层组件的onStateChange方法通过redux.store.subscribe绑定为回调函数 // 同时本层组件的onStateChange触发this.subscription.notifyNestedSubs方法 // 而子组件的onStateChange通过this.subscription.addNestedSub绑定为回调函数 // 这样redux.store.dispatch就可以触发父子组件的props更新 // 最终调用react组件的setState方法重绘组件 function match(arg,factories,name) { for (let i = factories.length - 1; i >= 0; i--) { const result = factories[i](arg) if (result) return result } 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函数通常为内部调用,各参数均为默认值;这样处理的好处是便于模块化,在各模块中设置默认值 export function createConnect({ connectHOC = connectAdvanced,mapStateToPropsFactories = defaultMapStateToPropsFactories,mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,mergePropsFactories = defaultMergePropsFactories,selectorFactory = defaultSelectorFactory } = {}) { // connect函数作为外部api接口,各参数均由用户传入;返回函数,接受待包装的react组件作为参数 // 通过内部调用connectHOC(selectorFactory,options)(basicComponent)构造基于react组件的高阶组件basicComponent // 该组件赋予selector属性,其中selector.run方法用于重新计算props // 计算props的过程,需要通过传入当前redux.store的dispatch方法、高阶组件属性options // 借此装饰用户配置mapStateToProps、mapDispatchToProps、mergeProps函数,mapDispatchToProps可以是对象 // 该组件赋予subscription属性,通过subscription.onStateChange作为redux.store.dispatch的直接或间接回调函数 // 而subscription.onStateChange内部调用react.selector.run重新计算样式值、react.setState重绘组件 // 注:用户配置的mapStateToProps、mapDispatchToProps为函数,可添加dependsOnOwnProps属性 // 当其为真值时,意味通过redux的state、dispatch更新react组件的props时,需要原有的props作为参数 // mapStateToProps将redux.store.state映射给props,store.state变动引起props相应变动 // 示例:redux.store.state改变,将引起props.todos属性作相应改变 // const mapStateToProps = (state) => { // return { // todos: getVisibleTodos(state.todos,state.visibilityFilter) // } // } // const getVisibleTodos = (todos,filter) => { // switch (filter) { // case 'SHOW_ALL': // return todos // case 'SHOW_COMPLETED': // return todos.filter(t => t.completed) // case 'SHOW_ACTIVE': // return todos.filter(t => !t.completed) // default: // throw new Error('Unknown filter: ' + filter) // } // } // mapDispatchToProps将redux.store.dispatch映射给props,便于通过props[actionname]调用redux.store.dispatch方法 // 示例1:可通过props.onClick()触发redux事件机制,即通过dispatch派发action // dispatch方法由装饰函数传入 // mapDispatchToProps为函数形式,待装饰,才可以传入dispatch方法 // const mapDispatchToProps = ( // dispatch,// ownProps // ) => { // return { // onClick: () => { // dispatch({ // type: 'SET_VISIBILITY_FILTER',// filter: ownProps.filter // }); // } // }; // } // 示例2:props.onClick()将通过react-redux内部机制自动调用dispatch派发action,action由mapDispatchToProps构造 // 装饰过程中调用redux的bindActionCreators生成用于派发相应action的方法,内部调用dispatch方法 // mapDispatchToProps为对象形式,才可以调用redux的bindActionCreators方法 // const mapDispatchToProps = { // onClick: (filter) => { // type: 'SET_VISIBILITY_FILTER',// filter: filter // }; // } return function connect( mapStateToProps,mapDispatchToProps,mergeProps,// 可重写的属性包括areStatesEqual、areOwnPropsEqual、areStatePropsEqual、areMergedPropsEqual // 以及methodName(错误提示用)、等 { // 真值时,redux.dispatch派发action事件触发时,将优化react.props计算方案 // 否值时,傻瓜式重新计算react.props pure = true,// 默认判断redux.state是否修改,需要重新将redux.state、redux.dispatch按条件赋值给react.props // 用户重写判断机制,可以实现修改redux.state部分属性时,避过react-redux通过redux.store重新计算props areStatesEqual = strictEqual,// 默认判断react.props是否修改,需要重新,需要重新将redux.state、redux.dispatch按条件赋值给react.props // 用户重写判断机制,可以实现修改react.props部分属性时,避过react-redux通过redux.store重新计算props areOwnPropsEqual = shallowEqual,// 由mapStateToProps函数获得的stateProps是否与前一次相同,可避免react.props的重新赋值 areStatePropsEqual = shallowEqual,// 由redux.state、redux.dispatch混合构造的mergeProps是否与前一次相同,可避免react.props的重新赋值 areMergedPropsEqual = shallowEqual,...extraOptions } = {} ) { // 当用户配置的mapStateToProps是函数时,使用wrapMapToPropsFunc装饰mapStateToProps // 传入redux的dispatch方法、react组件的displayName // 当用户配置的mapStateToProps不是函数时,设置默认的mergeProps为空函数;否则不设置 // 接受待装饰函数,返回装饰函数构造器,执行后装饰函数 // 数组项在connect模块的match函数中挨个调用,输出为真值时即返回,增加编码的灵活性Props为空函数;否则不设置 const initMapStateToProps = match(mapStateToProps,mapStateToPropsFactories,'mapStateToProps') // 当用户配置的mapDispatchToProps是函数时,使用wrapMapToPropsFunc装饰mapDispatchToProps // 传入redux的dispatch方法、react组件的displayName // 当用户配置的mapDispatchToProps不是函数时,设置默认的mapDispatchToProps为输出redux.store.dispatch方法;否则不设置 // 接受待装饰函数,返回装饰函数构造器,执行后装饰函数 // 数组项在connect模块的match函数中挨个调用,输出为真值时即返回,增加编码的灵活性 const initMapDispatchToProps = match(mapDispatchToProps,mapDispatchToPropsFactories,'mapDispatchToProps') // 当用户配置的mergeProps是函数时,使用wrapMergePropsFunc装饰mergeProps // 传入redux的dispatch方法、react组件的displayName、pure、areMergedPropsEqual // 当用户配置的mergeProps不是函数时,设置默认的mergeProps为defaultMergeProps // 接受待装饰函数,返回装饰函数构造器,执行后装饰函数 // 数组项在connect模块的match函数中挨个调用,输出为真值时即返回,增加编码的灵活性Props为空函数;否则不设置 const initMergeProps = match(mergeProps,mergePropsFactories,'mergeProps') return connectHOC(selectorFactory,{ methodName: 'connect',// 用于错误提示 // used to compute Connect's displayName from the wrapped component's displayName. getDisplayName: name => `Connect(${name})`,// react.store.dispatch(外部接口为react.props.actionname)触发改变props,是否重绘组件 shouldHandleStateChanges: Boolean(mapStateToProps),initMapStateToProps,initMapDispatchToProps,initMergeProps,pure,areStatesEqual,areOwnPropsEqual,areStatePropsEqual,areMergedPropsEqual,...extraOptions }) } } export default createConnect()
connectAdvanced.js
import hoistStatics from 'hoist-non-react-statics' import invariant from 'invariant' import { Component,createElement } from 'react' // 通过向react组件挂载this.subscription对象 // 将当前组件及其子孙组件的subscription.onStateChange绑定为redux.stroe.dispatch方法的回调 // subscription.onStateChange方法中,调用this.selector.run重新计算props // 本层组件的onStateChange方法通过redux.store.subscribe绑定为回调函数 // 同时本层组件的onStateChange触发this.subscription.notifyNestedSubs方法 // 而子组件的onStateChange通过this.subscription.addNestedSub绑定为回调函数 // 这样redux.store.dispatch就可以触发父子组件的props更新 import Subscription from '../utils/Subscription' import storeShape from '../utils/storeShape' let hotReloadingVersion = 0 // connectAdvanced函数由connect模块内部调用,铰链redux.store和react组件 // 目的是通过react.props获取redux.state、redux.dispatch(action)的相关特征,传输数据流和派发事件 // 同时获得redux.dispatch特征的props.actionname派发事件时,触发react.props重新计算和组件视图重绘 // 返回函数接受react组件构造函数为参数,用以形成高阶组件,作为视图层render方法渲染的参数 // 首参selectorFactory默认为selectorFactory模块 // 用于定义最终装饰函数mapStateToProps、mapDispatchToProps、mergeProps的执行机制 // 通过redux、react相关树形,构造react.props计算函数 // 次参options对象,相关配置 export default function connectAdvanced( selectorFactory,{ // the func used to compute this HOC's displayName from the wrapped component's displayName. // probably overridden by wrapper functions such as connect() getDisplayName = name => `ConnectAdvanced(${name})`,// 错误提示用 methodName = 'connectAdvanced',// if defined,the name of the property passed to the wrapped element indicating the number of // calls to render. useful for watching in react devtools for unnecessary re-renders. renderCountProp = undefined,// redux.store.state初始化赋值、redux.store.dispatch方法触发时引起store.state变更,是否影响组件的props // 默认影响,shouldHandleStateChanges置为false时不影响,即redux监听state变更不影响react视图渲染 // 用户使用react-redux,shouldHandleStateChanges由用户配置的mapStateToProps是否为真值决定 shouldHandleStateChanges = true,// the key of props/context to get the store storeKey = 'store',// if true,the wrapped element is exposed by this HOC via the getWrappedInstance() function. withRef = false,// additional options are passed through to the selectorFactory ...connectOptions } = {} ) { const subscriptionKey = storeKey + 'Subscription' const version = hotReloadingVersion++ const contextTypes = { [storeKey]: storeShape,[subscriptionKey]: PropTypes.instanceOf(Subscription),} const childContextTypes = { [subscriptionKey]: PropTypes.instanceOf(Subscription) } // 用户设置connect(mapStateToProps,mapDispatchToProps)(component)中component // 构成高阶组件 return function wrapWithConnect(WrappedComponent) { invariant( typeof WrappedComponent == 'function',`You must pass a component to the function returned by ` + `connect. Instead received ${WrappedComponent}` ) const wrappedComponentName = WrappedComponent.displayName || WrappedComponent.name || 'Component' const displayName = getDisplayName(wrappedComponentName) const selectorFactoryOptions = { ...connectOptions,getDisplayName,methodName,renderCountProp,shouldHandleStateChanges,storeKey,withRef,displayName,wrappedComponentName,WrappedComponent } class Connect extends Component { constructor(props,context) { super(props,context) this.version = version this.state = {} this.renderCount = 0 // Provider容器组件中,通过context.store向子组件传入Redux的store,storeKey默认为store // 未曾设置Provider的情形下呢???? this.store = this.props[storeKey] || this.context[storeKey] // 父组件的subscription回调队列属性,当前为顶层组件时为null this.parentSub = props[subscriptionKey] || context[subscriptionKey] this.setWrappedInstance = this.setWrappedInstance.bind(this) invariant(this.store,`Could not find "${storeKey}" in either the context or ` + `props of "${displayName}". ` + `Either wrap the root component in a <Provider>,` + `or explicitly pass "${storeKey}" as a prop to "${displayName}".` ) // getState引用当前组件相应Redux的store.getState this.getState = this.store.getState.bind(this.store); // 构建this.selector属性,其中props方法获取当前的props,run方法更新props(引用对象形式) this.initSelector() // 构建this.subscription回调函数队列,构建subscription.onStateChange方法,但不挂载 // componentDidMount方法中,顶层组件redux.store回调队列中挂载父子组件的onStateChange方法 // redux.store.dispatch方法执行时,触发父子组件的onStateChange方法 // 更新嵌套组件的props,同时按shouldComponentUpdate条件触发组件重绘 this.initSubscription() } // 将subscription回调队列属性传递给子组件,或者将祖先组件的subscription传递给子组件 getChildContext() { return { [subscriptionKey]: this.subscription || this.parentSub } } // 挂载父子组件的onStateChange方法,绑定dispatch方法触发时更新组件的props属性以及重绘组件 // connect方法执行过程中,由redux.store.state初始化数据更新组件props组件属性 componentDidMount() { if (!shouldHandleStateChanges) return // 顶层组件redux.store、this.subscrption回调队列中挂载父子组件的onStateChange方法 this.subscription.trySubscribe() // connect(mapStateToProps,mapDispatchToProps)方法执行时 // redux.store.state初始化绑定到react组件props属性过程中,是否更新组件props属性 // 以及重绘组件,默认不重绘??? this.selector.run(this.props) if (this.selector.shouldComponentUpdate) this.forceUpdate() } // 由组件更新后、重绘前的props,触发redux.store.dispatch方法更新props,用于重绘 componentWillReceiveProps(nextProps) { this.selector.run(nextProps) } // 判断组件是否已由redux机制进行重绘 shouldComponentUpdate() { return this.selector.shouldComponentUpdate } // 组件取消挂载时释放内存,即connect(mapStateToProps,mapDispatchToProps)(component)中component componentWillUnmount() { if (this.subscription) this.subscription.tryUnsubscribe() this.subscription = null this.store = null this.parentSub = null this.selector.run = () => {} } // 获取被包裹的UI组件 getWrappedInstance() { invariant(withRef,`To access the wrapped instance,you need to specify ` + `{ withRef: true } in the options argument of the ${methodName}() call.` ) return this.wrappedInstance } setWrappedInstance(ref) { this.wrappedInstance = ref } // 构建this.selector属性,其中props方法获取当前的props,run方法更新props // 通过redux.store.state更新、redux.store.dispatch方法触发获取最新的props initSelector() { const { dispatch } = this.store const { getState } = this; // 返回react.props计算函数,返回函数在redux.store.dispatch派发action事件时执行 const sourceSelector = selectorFactory(dispatch,selectorFactoryOptions) const selector = this.selector = { shouldComponentUpdate: true,props: sourceSelector(getState(),this.props),// 重新计算react.props run: function runComponentSelector(props) { try { const nextProps = sourceSelector(getState(),props) if (selector.error || nextProps !== selector.props) { selector.shouldComponentUpdate = true selector.props = nextProps selector.error = null } } catch (error) { selector.shouldComponentUpdate = true selector.error = error } } } } // 构建this.subscription回调函数队列,构建subscription.onStateChange方法,但不挂载 // 顶层组件subscription回调函数队列的onStateChange方法由本组件Redux.store.dispatch触发执行 // 子组件的onStateChange方法由顶层组件Redux.store.dispatch触发顶层组件相应的onStateChange方法 // 最终通过调用顶层组件subscription.notifyNestedSubs方法触发执行 // onStateChange方法用于更新嵌套组件的props,同时按shouldComponentUpdate条件触发组件重绘 initSubscription() { if (shouldHandleStateChanges) { const subscription = this.subscription = new Subscription(this.store,this.parentSub) const dummyState = {} // 在Subsciption模块中,通过redux.store.dispatch方法触发父子组件的this.subscription.onStateChange // 更新嵌套组件的props,同时按shouldComponentUpdate条件触发组件重绘 subscription.onStateChange = function onStateChange() { // 更新组件的props this.selector.run(this.props) // this.selector.shouldComponentUpdate为真值重绘当前组件,否则更新子组件的props、按条件重绘子组件 if (!this.selector.shouldComponentUpdate) { subscription.notifyNestedSubs() } else { // 更新组件完成后执行回调, this.componentDidUpdate = function componentDidUpdate() { this.componentDidUpdate = undefined subscription.notifyNestedSubs() } // 调用setState更新react组件,setState方法不论prevState、currentState改变与否 // 都会比较组件的props进行重绘??? this.setState(dummyState) } }.bind(this) } } // 判断顶层组件的store及subscription回调函数队列是否挂载父子组件的onStateChange方法 isSubscribed() { return Boolean(this.subscription) && this.subscription.isSubscribed() } // 通过selector.props添加额外的属性,当组件更新时,又会发生怎样的情况??? addExtraProps(props) { if (!withRef && !renderCountProp) return props // make a shallow copy so that fields added don't leak to the original selector. // this is especially important for 'ref' since that's a reference back to the component // instance. a singleton memoized selector would then be holding a reference to the // instance,preventing the instance from being garbage collected,and that would be bad const withExtras = { ...props } if (withRef) withExtras.ref = this.setWrappedInstance if (renderCountProp) withExtras[renderCountProp] = this.renderCount++ return withExtras } render() { const selector = this.selector // 初始化渲染时,以redux更新props后,不予的重绘??? selector.shouldComponentUpdate = false if (selector.error) { throw selector.error } else { return createElement(WrappedComponent,this.addExtraProps(selector.props)) } } } Connect.WrappedComponent = WrappedComponent Connect.displayName = displayName Connect.childContextTypes = childContextTypes Connect.contextTypes = contextTypes Connect.propTypes = contextTypes // 开发环境,保证props可由redux机制顺利更新,并完成组件重绘 if (process.env.NODE_ENV !== 'production') { Connect.prototype.componentWillUpdate = function componentWillUpdate() { if (this.version !== version) { this.version = version this.initSelector() if (this.subscription) this.subscription.tryUnsubscribe() this.initSubscription() if (shouldHandleStateChanges) this.subscription.trySubscribe() } } } return hoistStatics(Connect,WrappedComponent) } }
mapStateToProps.js、mapDispatchToProps.js、wrapMapToProps.js、mergeProps.js
// mapStateToProps.js import { wrapMapToPropsConstant,wrapMapToPropsFunc } from './wrapMapToProps' // 当用户配置的mapStateToProps是函数时,使用wrapMapToPropsFunc装饰mapStateToProps // 传入redux的dispatch方法、react组件的displayName export function whenMapStateToPropsIsFunction(mapStateToProps) { return (typeof mapStateToProps === 'function') ? wrapMapToPropsFunc(mapStateToProps,'mapStateToProps') : undefined } // 当用户配置的mapStateToProps不是函数时,设置默认的mapStateToProps为空函数;否则不设置 export function whenMapStateToPropsIsMissing(mapStateToProps) { return (!mapStateToProps) ? wrapMapToPropsConstant(() => ({})) : undefined } // 当用户配置的mapStateToProps是函数时,使用wrapMapToPropsFunc装饰mapStateToProps // 传入redux的dispatch方法、react组件的displayName // 当用户配置的mapStateToProps不是函数时,设置默认的mergeProps为空函数;否则不设置 // 接受待装饰函数,返回装饰函数构造器,执行后装饰函数 // 数组项在connect模块的match函数中挨个调用,输出为真值时即返回,增加编码的灵活性 export default [ whenMapStateToPropsIsFunction,whenMapStateToPropsIsMissing ] // mapDispatchToProps.js import { bindActionCreators } from 'redux' import { wrapMapToPropsConstant,wrapMapToPropsFunc } from './wrapMapToProps' // 当用户配置的mapDispatchToProps是函数时,使用wrapMapToPropsFunc装饰mapDispatchToProps // 传入redux的dispatch方法、react组件的displayName export function whenMapDispatchToPropsIsFunction(mapDispatchToProps) { return (typeof mapDispatchToProps === 'function') ? wrapMapToPropsFunc(mapDispatchToProps,'mapDispatchToProps') : undefined } // 当用户配置的mapDispatchToProps不是函数时,设置默认的mapDispatchToProps为输出redux.store.dispatch方法;否则不设置 export function whenMapDispatchToPropsIsMissing(mapDispatchToProps) { return (!mapDispatchToProps) ? wrapMapToPropsConstant(dispatch => ({ dispatch })) : undefined } // 当用户配置的mapDispatchToProps是对象时,调用bindActionCreators生成特定派发方法;否则返回undefined export function whenMapDispatchToPropsIsObject(mapDispatchToProps) { return (mapDispatchToProps && typeof mapDispatchToProps === 'object') ? wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps,dispatch)) : undefined } // 当用户配置的mapDispatchToProps是函数时,使用wrapMapToPropsFunc装饰mapDispatchToProps // 传入redux的dispatch方法、react组件的displayName // 当用户配置的mapDispatchToProps不是函数时,设置默认的mapDispatchToProps为输出redux.store.dispatch方法;否则不设置 // 接受待装饰函数,返回装饰函数构造器,执行后装饰函数 // 数组项在connect模块的match函数中挨个调用,输出为真值时即返回,增加编码的灵活性 export default [ whenMapDispatchToPropsIsFunction,whenMapDispatchToPropsIsMissing,whenMapDispatchToPropsIsObject ] // wrapMapToProps.js // 普通对象校验 import verifyPlainObject from '../utils/verifyPlainObject' // 当用户配置的mapDispatchToProps为对象形式时,在mapDispatchToProps模块的whenMapDispatchToPropsIsObject函数中调用 // 参数getConstant为dispatch => bindActionCreators(mapDispatchToProps,dispatch)函数 // 接受dispatch作为参数,生成特定派发action的方法,对象形式,最终挂载给react组件的props属性 // 即调用props.actionname派发特定的action // 返回函数形式;同用户配置的mapDispatchToProps为函数时统一接口,为函数时需要接受dispatch方法作为参数 // 函数返回值为对象形式,用于更新react组件的props属性 export function wrapMapToPropsConstant(getConstant) { return function initConstantSelector(dispatch,options) { const constant = getConstant(dispatch,options) function constantSelector() { return constant } // react-redux自动派发action时,不需要根据react原有的props去判断派发何种action constantSelector.dependsOnOwnProps = false return constantSelector } } // 通过redux的state、dispatch更新react组件的props时,是否需要原有的props作为参数,就此形成新的props // 用户配置的mapStateToProps、mapDispatchToProps为函数,可添加dependsOnOwnProps属性 // 当其为真值时,意味通过redux的state、dispatch更新react组件的props时,需要原有的props作为参数 export function getDependsOnOwnProps(mapToProps) { return (mapToProps.dependsOnOwnProps !== null && mapToProps.dependsOnOwnProps !== undefined) ? Boolean(mapToProps.dependsOnOwnProps) : mapToProps.length !== 1 } // 当用户配置的mapStateToProps(即mapStateToProps、mapDispatchToProps)为函数形式时 // 参数methodName校验提示用,由connect模块通过match方法调用mapStateToProps或mapDispatchToProps模块传入 // 在connect模块中构建initProxySelector函数 // initProxySelector在sconnectAdvanced模块中接受redux.dispatch、react.displayName作为参数 // 返回mapToPropsProxy封装函数 // mapToPropsProxy在selectorFactory模块中接受redux.state|dispatch、react.displayName作为参数 // 正式用户配置的mapStateToProps或mapDispatchToProps函数,更新react.props export function wrapMapToPropsFunc(mapToProps,methodName) { // selectorFactory模块中调用,返回值赋给mapStateToProps或mapDispatchToProps // 参数dispatch为容器store的dispatch方法,displayName为容器的displayName return function initProxySelector(dispatch,{ displayName }) { const proxy = function mapToPropsProxy(stateOrDispatch,ownProps) { return proxy.dependsOnOwnProps ? proxy.mapToProps(stateOrDispatch,ownProps) : proxy.mapToProps(stateOrDispatch) } // 判断通过redux的state、dispatch更新react组件的props时,是否需要原有的props作为参数,就此形成新的props proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps) // 参数stateOrDispatch、ownProps在selectorFactory模块中传入 // 针对mapStateToProps传入state,针对mapDispatchToProps传入dispatch // 通过proxy.mapToProps方法执行过程中按条件重写proxy.mapToProps,构成递归调用proxy.mapToProps // 第一次调用proxy.mapToProps,动态传入react组件原有的props // 第二次调用proxy.mapToProps,执行用户配置的mapStateToProps、mapDispatchToProps更新props // 第三次调用proxy.mapToProps,当mapDispatchToProps返回执行redux.store.dispatch的函数时 // 通过递归调用更新props proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch,ownProps) { // 重新设定proxy.mapToProps为用户传入的mapToProps函数(即mapStateToProps、mapDispatchToProps) // 当再次调用proxy函数时将获取mapToProps返回对象并执行 proxy.mapToProps = mapToProps let props = proxy(stateOrDispatch,ownProps) // 用户传入的mapToProps函数(即mapStateToProps、mapDispatchToProps)返回函数 // 通常是mapDispatchToProps,需要通过执行redux.store.dispatch更新props // 再次调用proxy获取该函数的返回对象 if (typeof props === 'function') { proxy.mapToProps = props proxy.dependsOnOwnProps = getDependsOnOwnProps(props) props = proxy(stateOrDispatch,ownProps) } // 校验props为普通对象 if (process.env.NODE_ENV !== 'production') verifyPlainObject(props,methodName) // 用户传入的mapToProps函数返回值,对象形式 // 即connect接口接受的mapStateToProps或mapDispatchToProps参数函数返回值 return props } return proxy } } // mergeProps.js // 普通对象校验 import verifyPlainObject from '../utils/verifyPlainObject' // 默认的mergeProps,原样输出stateProps,dispatchProps,ownProps export function defaultMergeProps(stateProps,ownProps) { return { ...ownProps,...stateProps,...dispatchProps } } // 装饰用户配置的mergeProps,传入redux的dispatch方法、react组件的displayName、pure、areMergedPropsEqual export function wrapMergePropsFunc(mergeProps) { // 将redux的dispatch方法、react组件的displayName、pure、areMergedPropsEqual等属性传入mergeProps // 用于警告提示或者mergeProps方法中使用,mergeProps中可直接使用dispatch方法 return function initMergePropsProxy( dispatch,{ displayName,areMergedPropsEqual } ) { let hasRunOnce = false let mergedProps// 缓存前一次混合mergedProps return function mergePropsProxy(stateProps,ownProps) { // mergeProps(stateProps,ownProps) // 将mapStateToProps获得的stateProps、mapDispatchToProps获得的dispatchProps、 // 以及react组件原有的ownProps,混合为新的props const nextMergedProps = mergeProps(stateProps,ownProps) // redux.dispatch事件触发时,傻瓜式重新计算混合props或优化计算props if (hasRunOnce) { if (!pure || !areMergedPropsEqual(nextMergedProps,mergedProps)) mergedProps = nextMergedProps // react和redux初始化关联时,返回初次计算生成的nextMergedProps } else { hasRunOnce = true mergedProps = nextMergedProps // 校验mergedProps为普通对象 if (process.env.NODE_ENV !== 'production') verifyPlainObject(mergedProps,'mergeProps') } return mergedProps } } } // 当用户配置的mergeProps是函数时,使用wrapMergePropsFunc装饰mergeProps // 传入redux的dispatch方法、react组件的displayName、pure、areMergedPropsEqual // 当用户配置的mergeProps不是函数时,返回undefined export function whenMergePropsIsFunction(mergeProps) { return (typeof mergeProps === 'function') ? wrapMergePropsFunc(mergeProps) : undefined } // 当用户配置的mergeProps不是函数时,设置默认的mergeProps为defaultMergeProps;否则不设置 export function whenMergePropsIsOmitted(mergeProps) { return (!mergeProps) ? () => defaultMergeProps : undefined } // 当用户配置的mergeProps是函数时,使用wrapMergePropsFunc装饰mergeProps // 传入redux的dispatch方法、react组件的displayName、pure、areMergedPropsEqual // 当用户配置的mergeProps不是函数时,设置默认的mergeProps为defaultMergeProps // 接受待装饰函数,返回装饰函数构造器,执行后装饰函数 // 数组项在connect模块的match函数中挨个调用,输出为真值时即返回,增加编码的灵活性 export default [ whenMergePropsIsFunction,whenMergePropsIsOmitted ]
selectorFactory.js、verifySubselectors.js
// selectorFactory.js // 开发环境校验最终装饰函数mapStateToProps、mapDispatchToProps、mergeProps为真值 // 提示最终装饰函数mapStateToProps、mapDispatchToProps不需要react组件原有props传参 import verifySubselectors from './verifySubselectors' // redux.dispatch派发action事件触发时,傻瓜式重新计算react.props export function impureFinalPropsSelectorFactory( mapStateToProps,dispatch ) { return function impureFinalPropsSelector(state,ownProps) { return mergeProps( mapStateToProps(state,ownProps),mapDispatchToProps(dispatch,ownProps ) } } // 定义最终装饰函数mapStateToProps、mapDispatchToProps、mergeProps的执行机制 // 按条件分为初始化执行,调用handleFirstCall函数;再次执行,调用pureFinalPropsSelector // 并传入redux相关的state、dispatch属性,react相关的ownProps属性 // 返回函数,用于重新获取react组件的props // 与impureFinalPropsSelectorFactory的差别时 // impureFinalPropsSelectorFactory每次触发redux.dispatch,均重新计算react.props // pureFinalPropsSelectorFactory是优化计算方案,state、props改变时才重新计算 export function pureFinalPropsSelectorFactory( mapStateToProps,dispatch,{ areStatesEqual,areStatePropsEqual } ) { let hasRunAtLeastOnce = false// redux.store同react.props是否关联过一次标识 let state// 缓存上一次redux.store.state let ownProps// 缓存上一次react.props let stateProps// 缓存上一次redux.store.state赋值给react.props的属性 let dispatchProps// 缓存上一次redux.store.dispatch构造的特定派发action方法赋值给react.props的属性 let mergedProps// 缓存stateProps、dispatchProps、ownProps复合集,react组件待更新的props // 初始化将redux.store.state、redux.store.dispatch按条件赋值给react.props function handleFirstCall(firstState,firstOwnProps) { state = firstState ownProps = firstOwnProps // 将redux.store.state赋值给react.props,特定情况下需要react组件原有的props设定赋值情况 stateProps = mapStateToProps(state,ownProps) // 将redux.store.dispatch构造的特定派发action方法赋值给react.props // 特定情况下需要react组件原有的props设定赋值情况 dispatchProps = mapDispatchToProps(dispatch,ownProps) // 混合stateProps、dispatchProps赋值给react.props,特定情况下需要react组件原有的props设定赋值情况 mergedProps = mergeProps(stateProps,ownProps) hasRunAtLeastOnce = true return mergedProps } // redux的dispatch触发且react组件props改变时,重新获取redux.store.state、redux.store.dispatch相应属性 // 按条件赋值给react.props function handleNewPropsAndNewState() { // 因redux的dispatch触发、react组件props改变,重新将redux.store.state绑定在react.props属性上 stateProps = mapStateToProps(state,ownProps) // 因react.props变动,重新将redux.dispatch特定的派发方法绑定在react.props属性上 if (mapDispatchToProps.dependsOnOwnProps) dispatchProps = mapDispatchToProps(dispatch,ownProps) mergedProps = mergeProps(stateProps,ownProps) return mergedProps } // react组件props改变时,重新获取redux.store.state、redux.store.dispatch相应属性,按条件赋值给react.props function handleNewProps() { if (mapStateToProps.dependsOnOwnProps) stateProps = mapStateToProps(state,ownProps) return mergedProps } // redux的dispatch触发,引起state改变时,重新获取redux.store.state,按条件赋值给react.props // redux机制,dispatch(action)触发事件后,state在回调函数执行前已改变 function handleNewState() { const nextStateProps = mapStateToProps(state,ownProps) const statePropsChanged = !areStatePropsEqual(nextStateProps,stateProps) stateProps = nextStateProps if (statePropsChanged) mergedProps = mergeProps(stateProps,ownProps) return mergedProps } // redux.dispatch事件触发时,重新将redux.store.state、redux.store.dispatch按条件赋值给react.props // react.props改变需要在redux.dispatch事件触发过程中发生 function handleSubsequentCalls(nextState,nextOwnProps) { // 默认判断react.props是否修改,需要重新将redux.state、redux.dispatch按条件赋值给react.props // 用户重写判断机制,可以实现修改react.props部分属性时,避过react-redux通过redux.store重新计算props const propsChanged = !areOwnPropsEqual(nextOwnProps,ownProps) // 默认判断redux.state是否修改,需要重新将redux.state、redux.dispatch按条件赋值给react.props // 用户重写判断机制,可以实现修改redux.state部分属性时,避过react-redux通过redux.store重新计算props const stateChanged = !areStatesEqual(nextState,state) state = nextState ownProps = nextOwnProps // redux的dispatch触发且react组件props改变时,重新获取redux.store.state、redux.store.dispatch相应属性 // 按条件赋值给react.props if (propsChanged && stateChanged) return handleNewPropsAndNewState() // react组件props改变时,重新获取redux.store.state、redux.store.dispatch相应属性,按条件赋值给react.props if (propsChanged) return handleNewProps() // redux的dispatch触发,引起state改变时,重新获取redux.store.state,按条件赋值给react.props if (stateChanged) return handleNewState() // redux的dispatch未曾触发且react组件props未曾改变,直接返回前一次props return mergedProps } // 返回函数,优点是可以使用闭包变量缓存前次的state及ownProps // 定义最终装饰函数mapStateToProps、mapDispatchToProps、mergeProps的执行机制 // 传入redux相关的state、dispatch属性,react相关的ownProps属性 return function pureFinalPropsSelector(nextState,nextOwnProps) { return hasRunAtLeastOnce ? handleSubsequentCalls(nextState,nextOwnProps) : handleFirstCall(nextState,nextOwnProps) } } // connectAdvanced模块中调用,通过connect模块传入,返回react.props计算函数 // 参数dispatch为redux.store的dispatch方法,次参对象为react组件的属性 // 各参数传给装饰函数initMapStateToProps、initMapDispatchToProps、initMergeProps // 获取mapStateToProps、mapDispatchToProps、mergeProps计算react组件待变更的props export default function finalPropsSelectorFactory(dispatch,{ initMapStateToProps,...options }) { // 获取最终装饰函数mapStateToProps、mapDispatchToProps、mergeProps,用以计算计算react组件待变更的props const mapStateToProps = initMapStateToProps(dispatch,options) const mapDispatchToProps = initMapDispatchToProps(dispatch,options) const mergeProps = initMergeProps(dispatch,options) // 开发环境校验最终装饰函数mapStateToProps、mapDispatchToProps、mergeProps为真值 // 提示最终装饰函数mapStateToProps、mapDispatchToProps不需要react组件原有props传参 if (process.env.NODE_ENV !== 'production') { verifySubselectors(mapStateToProps,options.displayName) } // options.pure在connect模块中设置默认为真值 const selectorFactory = options.pure ? pureFinalPropsSelectorFactory// 傻瓜式计算react.props : impureFinalPropsSelectorFactory// 优化计算react.props // 定义最终装饰函数mapStateToProps、mapDispatchToProps、mergeProps调用机制,并传入dispatch、options参数 // 返回react.props计算函数 return selectorFactory( mapStateToProps,options ) } // verifySubselectors.js import warning from '../utils/warning' function verify(selector,displayName) { // 校验最终装饰函数mapStateToProps、mapDispatchToProps、mergeProps为真值 if (!selector) { throw new Error(`Unexpected value for ${methodName} in ${displayName}.`) // 提示最终装饰函数mapStateToProps、mapDispatchToProps不需要react组件原有props传参 } else if (methodName === 'mapStateToProps' || methodName === 'mapDispatchToProps') { if (!selector.hasOwnProperty('dependsOnOwnProps')) { warning( `The selector for ${methodName} of ${displayName} did not specify a value for dependsOnOwnProps.` ) } } } export default function verifySubselectors(mapStateToProps,displayName) { verify(mapStateToProps,'mapStateToProps',displayName) verify(mapDispatchToProps,'mapDispatchToProps',displayName) verify(mergeProps,'mergeProps',displayName) }
Subscription.js
const CLEARED = null const nullListeners = { notify() {} } // 类似jquery的Callbacks回调函数队列,用于向队列next中添加和移除回调函数,以及触发回调函数的执行 function createListenerCollection() { let current = [] let next = [] return { clear() { next = CLEARED current = CLEARED },notify() { const listeners = current = next for (let i = 0; i < listeners.length; i++) { listeners[i]() } },subscribe(listener) { let isSubscribed = true if (next === current) next = current.slice() next.push(listener) return function unsubscribe() { if (!isSubscribed || current === CLEARED) return isSubscribed = false if (next === current) next = current.slice() next.splice(next.indexOf(listener),1) } } } } // 通过向react组件挂载this.subscription对象 // 将当前组件及其子孙组件的subscription.onStateChange绑定为redux.stroe.dispatch方法的回调 // subscription.onStateChange方法中,调用this.selector.run重新计算props // 本层组件的onStateChange方法通过redux.store.subscribe绑定为回调函数 // 同时本层组件的onStateChange触发this.subscription.notifyNestedSubs方法 // 而子组件的onStateChange通过this.subscription.addNestedSub绑定为回调函数 // 这样redux.store.dispatch就可以触发父子组件的props更新 export default class Subscription { constructor(store,parentSub) { this.store = store this.parentSub = parentSub this.unsubscribe = null this.listeners = nullListeners } // 添加回调函数,默认内部subscription方法使用 // 向父组件的subscription回调函数队列添加子组件subscription对象的onStateChange方法 addNestedSub(listener) { this.trySubscribe() return this.listeners.subscribe(listener) } // 触发回调函数执行,默认connectAdvanced模块构建的onStateChange方法内使用 // 父组件的subscription回调函数队列触发子组件subscription对象的onStateChange方法 notifyNestedSubs() { this.listeners.notify() } // 顶层组件的store及subscription回调函数队列是否挂载父子组件的onStateChange方法 isSubscribed() { return Boolean(this.unsubscribe) } // 挂载this.onStateChange方法,用于重新获得props,触发this.setState更新组件 // 当前组件为顶层组件,this.store挂载this.onStateChange方法 // 当前组件为子组件,通过父组件的onStateChange调用subscription.notifyNestedSubs挂载this.onStateChange方法 trySubscribe() { if (!this.unsubscribe) { // this.onStateChange is set by connectAdvanced.initSubscription() // 当前组件为顶层组件时,通过this.store.subscribe挂载this.onStateChange方法 // 即通过this.store.dispatch方法触发state改变的同时,同时触发this.onStateChange方法 // 当前组件为子组件时,通过父组件的onStateChange调用subscription.notifyNestedSubs // 触发子组件的onStateChange方法 // 怎样更新组件??? this.unsubscribe = this.parentSub ? this.parentSub.addNestedSub(this.onStateChange) : this.store.subscribe(this.onStateChange) this.listeners = createListenerCollection() } } tryUnsubscribe() { if (this.unsubscribe) { this.unsubscribe() this.unsubscribe = null this.listeners.clear() this.listeners = nullListeners } } }
shallowEqual.js、verifyPlainObject.js、warning.js、storeShape.js
// shallowEqual.js、verifyPlainObject.js、warning.js、storeShape.js const hasOwn = Object.prototype.hasOwnProperty // 比较变量是否全等,或者普通对象单层比较是否相同 export default function shallowEqual(a,b) { if (a === b) return true let countA = 0 let countB = 0 for (let key in a) { if (hasOwn.call(a,key) && a[key] !== b[key]) return false countA++ } for (let key in b) { if (hasOwn.call(b,key)) countB++ } return countA === countB } // verifyPlainObject.js import isPlainObject from 'lodash/isPlainObject' import warning from './warning' // 校验value是否普通对象 export default function verifyPlainObject(value,methodName) { if (!isPlainObject(value)) { warning( `${methodName}() in ${displayName} must return a plain object. Instead received ${value}.` ) } } // warning.js // 打印错误或报错 export default function warning(message) { /* eslint-disable no-console */ if (typeof console !== 'undefined' && typeof console.error === 'function') { console.error(message) } /* eslint-enable no-console */ try { throw new Error(message) /* eslint-disable no-empty */ } catch (e) {} /* eslint-enable no-empty */ } // storeShape.js import { PropTypes } from 'react' export default PropTypes.shape({ subscribe: PropTypes.func.isrequired,dispatch: PropTypes.func.isrequired,getState: PropTypes.func.isrequired })