今天看了下react-redux
的源码,主要来看下connect
的方法, 当前版本5.1.0
首先找到connect
的入口文件。在src/index.js
下找到。对应connect
文件夹下的connect.js
文件。
具体细节 看下面部分的源码 :
Copy export function createConnect ({
connectHOC = connectAdvanced ,
mapStateToPropsFactories = defaultMapStateToPropsFactories ,
mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories ,
mergePropsFactories = defaultMergePropsFactories ,
selectorFactory = defaultSelectorFactory
} = {}) {
return function connect (
mapStateToProps ,
mapDispatchToProps ,
mergeProps ,
{
pure = true ,
areStatesEqual = strictEqual ,
areOwnPropsEqual = shallowEqual ,
areStatePropsEqual = shallowEqual ,
areMergedPropsEqual = shallowEqual ,
... extraOptions
} = {}
) {
const initMapStateToProps = match (mapStateToProps , mapStateToPropsFactories , 'mapStateToProps' )
const initMapDispatchToProps = match (mapDispatchToProps , mapDispatchToPropsFactories , 'mapDispatchToProps' )
const initMergeProps = match (mergeProps , mergePropsFactories , 'mergeProps' )
return connectHOC (selectorFactory , {
// used in error messages
methodName : 'connect' ,
// used to compute Connect's displayName from the wrapped component's displayName.
getDisplayName : name => `Connect( ${ name } )` ,
// if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
shouldHandleStateChanges : Boolean (mapStateToProps) ,
// passed through to selectorFactory
initMapStateToProps ,
initMapDispatchToProps ,
initMergeProps ,
pure ,
areStatesEqual ,
areOwnPropsEqual ,
areStatePropsEqual ,
areMergedPropsEqual ,
// any extra options args can override defaults of connect or connectAdvanced
... extraOptions
})
}
}
我们知道,在容器组件平时对外暴露的时候是
```js const mapStateToProps = (state, ownProps) => ({ // ... })
const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(Object.assign({}, actions), dispatch) })
export default connect( mapStateToProps, mapDispatchToProps )(YourComponent)
Copy 我们可以看到基本都是都是传递两个参数,`mapStateToProps&mapDispatchToProps`, 第三个参数`mergeProps `并没有传递,这个参数是干什么的?
他在
```js
const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')
这里初始化mergeProps
,通过mergePropsFactories
来处理 里返回的两个函数来处理。 对应了两个函数对应了mergeProps
的两种情况:
whenMergePropsIsFunction
存在mergeProps
并且是function
的时候,会进行处理并返回一个函数 , 这个函数的第一个参数是一个dispatch
,第二个参数是一个对象,并且对象接收三个属性,如果不是一个方法,那就返回undefined
.
whenMergePropsIsOmitted
方法会判断mergeProps
是不是存在,如果存在就返回undefined
,否则就会返回一个默认的mergeProps
,可以发现,是在函数里调用的默认的mergeProps
.
mapStateToProps&mapDispatchToProps
和mergeProps
的初始化类似,都会进行判断再去操作。
他们的初始化会返回一个函数:
initMapStateToProps & initMapDispatchToProps
这两个实际上是wrapMapToProps.js
中的wrapMapToPropsFunc
函数返回的一个initProxySelector
函数.
initMergeProps
如果是空mergeProps
参数就使用默认的,如果mergeProps
参数是一个函数,那就使用mergeProps.js
的wrapMergePropsFunc
并且返回initMergePropsProxy
函数。
然后connect
后面的第四个参数是一个对象,他接收一些属性。
然后就是下一步了,在使用的时候,我们connect()
之后就是继续调用,传参是我们的容器组件。那这时,在源码里就是对应的return connectHOC(selectorFactory, {...})
, 这个返回的是一个函数运行的结果,所以我们传参的容器组件这个参数,并不是这里的selectorFactory
,而是他运行完成之后的结果所接受的参数。
上面有提到createHOC
对应的是connectAdvanced
.然后可以看到createHOC
传参的第一个是selectorFactory
, 就是对应的defaultSelectorFactory
,然后第二个参数是一些对象。
接下来我们就可以看看对应createHOC
的connectAdvanced
方法 了。
首先看connectAdvanced
在参数方面,第一个保持不变,第二个进行了扩展,添加了几个属性。 我们还是从对外暴露的接口来看,他直接暴露的是connectAdvanced
方法。因为我们在createHOC
所需要的是一个结果, 所以我们通过源码看看他是怎么运行的 .
在这个方法里,有返回一个方法wrapWithConnect(WrappedComponent){}
,好家伙,到这里我们可以知道connect
里的return createHOC(...)
的结果返回 的就是这个,然后再看返回的这个函数名称,很见名知意用connect包裹的函数的参数是包裹的组件
.那就看看这个函数里面是啥。
一些基本的处理,然后就是一个connect
基于React.Component
的继承,最后是返回return hoistStatics(Connect, WrappedComponent)
, 这个hoistStatics
是一个类似Object.assign
的copy
非react
的static property
. 换句话说,就是把传递的容器组件自定义的静态属性附加到connect
组件上去。
我们再看看这个connect class
, 他在构造函数里直接运行了this.initSelector()和this.initSubscription()
我们先看下initSelector
:
Copy initSelector () {
// 这里的sourceSelector 就是`selectFactory`里的返回的闭包(pureFinalPropsSelector),
// 在运行返回闭包之前已经处理了`initMapStateToProps`,并且那个地方返回的是`proxy`, 有标记
// selectorFactory会在返回pureFinalPropsSelector函数(handleSubsequentCalls函数或者handleFirstCall函数)
const sourceSelector = selectorFactory ( this . store .dispatch , selectorFactoryOptions)
//返回selector对象,包含一个run函数
this .selector = makeSelectorStateful (sourceSelector , this .store)
this . selector .run ( this .props)
}
别急,我们慢慢的看下去。发现就是使用的selectorFactory
方法来处理的 , 我们就看看这个方法是怎么处理的。对外暴露的方法在这里 , 他会根据pure
属性来确定到底该如何处理。其实我们从这一段注释就可以知道了:
Copy // If pure is true, the selector returned by selectorFactory will memoize its results,
// allowing connectAdvanced's shouldComponentUpdate to return false if final
// props have not changed. If false, the selector will always return a new
// object and shouldComponentUpdate will always return true.
就是去处理到底是否应该重新渲染,所有如果你想要刷新的情况,可以在connect
的传参的第四个对象里改变pure
为false
.
假设这个是一个pure
,那么就会继续调用pureFinalPropsSelectorFactory
。看这个参数:
Copy // 下面的这个调用是在闭包的返回基础上调用,见`connect.js`的`createConnect`方法里
// 下面的这个initMapStateToProps是相当于运行的闭包,即`wrapMapToProps`里的`initProxySelector`.
// 但是这几乎等于没有运行,因为这里直接返回了proxy函数.
const mapStateToProps = initMapStateToProps (dispatch , options) // return proxy
const mapDispatchToProps = initMapDispatchToProps (dispatch , options)
const mergeProps = initMergeProps (dispatch , options)
const selectorFactory = options .pure
? pureFinalPropsSelectorFactory
: impureFinalPropsSelectorFactory
// 上面返回了mapToProps之后,实则是返回了proxy.
return selectorFactory (
mapStateToProps ,
mapDispatchToProps ,
mergeProps ,
dispatch ,
options
)
好的,我们再看pureFinalPropsSelectorFactory
函数:
Copy return function pureFinalPropsSelector (nextState , nextOwnProps) {
return hasRunAtLeastOnce
? handleSubsequentCalls (nextState , nextOwnProps)
: handleFirstCall (nextState , nextOwnProps)
}
这个函数太长,我们看他返回的。那么我们直接在看看他调用的handleFirstCall
函数:
Copy function handleFirstCall (firstState , firstOwnProps) {
state = firstState
ownProps = firstOwnProps
// 这里实际上是在调用`wrapMapToProps -> wrapMapToPropsFunc`返回的`proxy`函数
//调用proxy函数会返回props,最后把这些props给merged并返回。
stateProps = mapStateToProps (state , ownProps)
dispatchProps = mapDispatchToProps (dispatch , ownProps)
mergedProps = mergeProps (stateProps , dispatchProps , ownProps)
hasRunAtLeastOnce = true
return mergedProps
}
好吧,这里看出来了吗?mergedProps
,这又是啥,我们来看看上面说到的proxy
函数:
Copy 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 ownProps
proxy .dependsOnOwnProps = true
proxy . mapToProps = function detectFactoryAndVerify (stateOrDispatch , ownProps) {
//这里是重点,把mapToProps指向`connect.js`里传递进来的`mapXxToProps`,也就是我们组建写的:
// const mapStateToProps = (state, ownProps) => ({myNeedProps: state.yourState})
// 不重写会死循环
proxy .mapToProps = mapToProps
proxy .dependsOnOwnProps = getDependsOnOwnProps (mapToProps)
let props = proxy (stateOrDispatch , ownProps)
if ( typeof props === 'function' ) {
proxy .mapToProps = props
proxy .dependsOnOwnProps = getDependsOnOwnProps (props)
props = proxy (stateOrDispatch , ownProps)
}
if ( process . env . NODE_ENV !== 'production' )
verifyPlainObject (props , displayName , methodName)
return props
}
return proxy
}
}
可以看到吧,proxy
会给你返回props
。
然后initSelector
同时也会为当前对象创建一个selector
: 我们结合上面的initSelector
可以知道,sourceSelector
就是selectorFactory
返回的handleSubsequentCalls
函数或者handleFirstCall
函数:
Copy function makeSelectorStateful (sourceSelector , store) {
// wrap the selector in an object that tracks its results between runs.
const selector = {
// props 就是this.props,然后会调用sourceSelector进行mergeProps
run : function runComponentSelector (props) {
try {
const nextProps = sourceSelector ( store .getState () , props)
if (nextProps !== selector .props || selector .error) {
selector .shouldComponentUpdate = true
selector .props = nextProps
selector .error = null
}
} catch (error) {
selector .shouldComponentUpdate = true
selector .error = error
}
}
}
return selector
}
最后来run
,如果符合条件或者出错,便会改变shouldComponentUpdate = true
,去重新 render
.
再来看看initSubscription
方法:
Copy initSubscription () {
if ( ! shouldHandleStateChanges) return
const parentSub = ( this .propsMode ? this .props : this .context)[subscriptionKey]
this .subscription = new Subscription ( this .store , parentSub , this . onStateChange .bind ( this ))
this .notifyNestedSubs = this . subscription . notifyNestedSubs .bind ( this .subscription)
}
先判断shouldHandleStateChanges
是不是成立,如果成立则进行,否则返回。成立会进行初始化,正如其名,初始化订阅相关事件。
这里初始化结束之后,我们看下componentDidMount
方法 ,
Copy componentDidMount () {
if ( ! shouldHandleStateChanges) return
// componentWillMount fires during server side rendering, but componentDidMount and
// componentWillUnmount do not. Because of this, trySubscribe happens during ...didMount.
// Otherwise, unsubscription would never take place during SSR, causing a memory leak.
// To handle the case where a child component may have triggered a state change by
// dispatching an action in its componentWillMount, we have to re-run the select and maybe
// re-render.
this . subscription .trySubscribe ()
this . selector .run ( this .props)
if ( this . selector .shouldComponentUpdate) this .forceUpdate ()
}
他也会和initSubscription
一样的去判断,确定是否进行下去。下面会进行尝试订阅 . 然后去运行selector
,运行selector
之后,会判断shouldComponentUpdate
,如果成立,则会进行forceUpdate
, 注意: forceUpdate
会跳过shouldComponentUpdate
的判断.
最后看下render
方法,
Copy render () {
const selector = this .selector
selector .shouldComponentUpdate = false
if ( selector .error) {
throw selector .error
} else {
return createElement (WrappedComponent , this .addExtraProps ( selector .props))
}
}
可以看到.他会对属性进行重写成false
,如果报错,就跑出错误,如果正常,那就进行render
创建元素到页面。
总结
provider 这个主要就是为了提供一个上下文的store
,使得下级的组件不需要props
可以直接获取store
。
mapStateToProps&mapDispatchToProps
这两个就是把对象放在props
里。 主要的操作就是通过wrapMapToProps.js
中的wrapMapToPropsFunc
函数去处理,在闭包proxy
中代理我们传入的mapToProps
方法去返回合并的props
。 这个调用的proxy
函数的是在connectAdvanced.js
的makeSelectorStateful
函数中,会传入一个store.getState()
。至于dispatch
则在initSelector
初始化的时候就传递进去了。
mergeProps
就是把这些props
给合并起来。
没有使用setState
,是如何改变状态进行渲染的
他会对connectAdvanced.js
的onStateChange
进行订阅,进行selector.run
去对比前后的props
。因为我们知道redux
的dispatch
代码里有执行 订阅相关的操作。 这样每次dispatch
之后都会去判断shouldComponentUpdate
,如果是就会去this.setState(dummyState)
去render
。