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