React-redux源码总共分为两部分,第一是Provider
,第二是connect
。
Provider
Provider比较简单,主要代码如下:
class Provider extends Component {
getChildContext() {
return { [storeKey]: this[storeKey], [subscriptionKey]: null }
}
constructor(props, context) {
super(props, context)
this[storeKey] = props.store;
}
render() {
return Children.only(this.props.children)
}
}
即通过context api将store传到子组件里面去。
connect
connect
算是一个比较有难度的部分,函数原型是:
connect(mapStateToProps,mapDispatchToProps,[mergeProps]) => (WrappedComponent) => HOC
我们尝试对其进行由上到下的分析:
入口
函数入口就是一个三阶函数:
export function createConnect({
connectHOC,
mapStateToPropsFactories,
mapDispatchTpropsFactories,
mergePropsFactories,
selectFactory
}) => connect(mapStateToProps,mapDispatchToProps,mergeProps, {/*...*/}) => {
const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps')
const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')
return connectHOC(selectorFactory, {
initMapStateToProps,
initMapDispatchToProps,
initMergeProps,
//...
})
}
export default createConnect()
写的其实比较绕,但是理解起来不难,首先createConnect
方法只相当于一个包装而已,将高阶组件connectHOC,以及一些工厂函数引入到connect
的父scope中来。
然后在connect
中,通过match函数来选择适当的factory来包装外界传入的参数)生成三个函数: initMapStateToProps
,initMapDispatchToProps
和initMergeProps
。
match函数的源码:
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}.`)
}
}
这段代码其中arg
就是connect函数接收的mapStateToProps
等,以倒序的方式寻找第一个返回非空的factory。我们可以先暂且不去深入推敲。
ConnectHOC
connectHOC
是整个react-redux的核心,在分析源代码之前,我们先看看一个工具函数:
function makeSelectorStateful(sourceSelector, store) {
const selector = {
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
}
selector主要作用是对redux的state和dispatch与react的props进行合并(mergeProps
)
这段代码主要是对sourceSelector
进行封装,增加了错误处理,新旧值的比较等。
下面是connectHOC
的主要源码:
// ...
export default function connectAdvanced(
selectorFactory,
// options object:
{
//省略掉的参数: withRef,displayName,renderCountProp,shouldHandleStateChanges,storeKey,getDisplayName
// 传入selectorFactory中额外的参数
// 主要包括 `initMapStateToProps`,`initMapDispatchToProps`和`initMergeProps`
...connectOptions
} = {}
) {
const subscriptionKey = storeKey + 'Subscription'
// ...
return function wrapWithConnect(WrappedComponent) {
// ...
const selectorFactoryOptions = {
...connectOptions,
//...
WrappedComponent
}
// 声明一个React组件
class Connect extends Component {
constructor(props, context) {
super(props, context)
// ...
// 从props或者context里面获得redux store
this.store = props[storeKey] || context[storeKey]
this.propsMode = Boolean(props[storeKey])
this.initSelector()
this.initSubscription()
}
initSelector() {
// 结合mapPropsTo...和mergeProps函数处理参数。(state, props) => nextProps
const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
// makeSelectorStateful: 包装sourceSelector函数,比较props和nextProps确定shouldComponentUpdate
this.selector = makeSelectorStateful(sourceSelector, this.store)
// 更新当前参数,实际上就是ownProps
this.selector.run(this.props)
}
initSubscription() {
// 如果不需要相应redux state变化,则不需要订阅
if (!shouldHandleStateChanges) return
// 获取祖先传下来的订阅对象
const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey]
// 生成当前组件的订阅对象,传入parentSub。
// 当parentSub不为空的时候,onStateChange实际上是注册到了parentSub的listeners数组,由组件手动调用notifyNestedSubs执行。
// 否则直接使用store.subscribe注册到redux中,由redux控制执行
// 这样做能始终保证祖先节点的更新早于子孙节点
this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this))
this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription)
}
onStateChange() {
// 更新props
this.selector.run(this.props)
if (!this.selector.shouldComponentUpdate) {
// 如果当前组件不需要更新,那么直接更新子组件
this.notifyNestedSubs()
} else {
// 先更新当前组件,再更新子组件
this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate
// dummyState 为空,意味触发react的更新机制
this.setState(dummyState)
}
}
getChildContext() {
// 如果store是从props中获得,那么直接将祖先的subscription对象传下去。(仅作为一个传话人,不响应父组件的dispatch事件)
// 否则,将自身的subscription传下去,形状如下
// Component A (B's subscription's parentSub == A)
// |
// -> Component B (C's subscription's parentSub == B)
// |
// -> Component C (E's subscription's parentSub == C)
// |
// |== Component D (propsMode === true) directly transfer C's sub context to E
// |
// -> Component E
// when a dispatch occured in Component A,the callback exec order will be A -> B -> C -> E
const subscription = this.propsMode ? null : this.subscription
return { [subscriptionKey]: subscription || this.context[subscriptionKey] }
}
componentDidMount() {
if (!shouldHandleStateChanges) return
// 因为componentWillMount在ssr中执行,而componentDidMount和componentWillUnmount不执行。因此如果将trySubscribe方法放在componentWillMount中的话,在ssr中可能会因为没有调用componentWillUnmount的unsubscribe而内存泄漏
this.subscription.trySubscribe()
// run即update,更新selector.nextProps,selector.shouldComponentUpdate
this.selector.run(this.props)
// 如果子组件在componentWillMount的时候dispatch了一个action,因而没有被上文的subscription捕获到,此时的selector就会检测出参数的不一致(shouldComponentUpdate = true),所以需要强制更新
if (this.selector.shouldComponentUpdate) this.forceUpdate()
}
// 更新props
componentWillReceiveProps(nextProps) {
this.selector.run(nextProps)
}
shouldComponentUpdate() {
return this.selector.shouldComponentUpdate
}
componentWillUnmount() {
if (this.subscription) this.subscription.tryUnsubscribe()
// 为什么直接使用subscription.notifyNestedSubs而要拷贝出来分别处理:
// 防止在notify过程中subscription为null,而null.notifyNestedSubs报错
this.subscription = null
this.notifyNestedSubs = noop
this.store = null
this.selector.run = noop
this.selector.shouldComponentUpdate = false
}
notifyNestedSubsOnComponentDidUpdate() {
// 避免多次调用
this.componentDidUpdate = undefined
this.notifyNestedSubs()
}
isSubscribed() {
return Boolean(this.subscription) && this.subscription.isSubscribed()
}
addExtraProps(props) {
// 如果没有额外的参数,直接返回props
if (!withRef && !renderCountProp && !(this.propsMode && this.subscription)) return props
// 防御性拷贝
const withExtras = { ...props }
if (withRef) withExtras.ref = this.setWrappedInstance
if (renderCountProp) withExtras[renderCountProp] = this.renderCount++
if (this.propsMode && this.subscription) withExtras[subscriptionKey] = this.subscription
return withExtras
}
render() {
const selector = this.selector
selector.shouldComponentUpdate = false
if (selector.error) {
throw selector.error
} else {
return createElement(WrappedComponent, this.addExtraProps(selector.props))
}
}
}
// ...
// 仅在开发环境下支持的的热重载
if (process.env.NODE_ENV !== 'production') {
Connect.prototype.componentWillUpdate = function componentWillUpdate() {
// We are hot reloading!
if (this.version !== version) {
this.version = version
this.initSelector()
let oldListeners = [];
if (this.subscription) {
oldListeners = this.subscription.listeners.get()
this.subscription.tryUnsubscribe()
}
this.initSubscription()
if (shouldHandleStateChanges) {
this.subscription.trySubscribe()
oldListeners.forEach(listener => this.subscription.listeners.subscribe(listener))
}
}
}
}
// 将WrappedComponent中的非React特定的静态属性(例如propTypes就是React的特定静态属性)赋值到Connect。
// 作用有点类似于Object.assign,但是仅复制非React特定的静态属性。
return hoistStatics(Connect, WrappedComponent)
}
}
至此,大部分逻辑我们已经很清楚了。现在我们来分析一下各种factory所做的事情:
selectorFactory
首先看主体:
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
: impureFinalPropsSelectorFactory
return selectorFactory(
mapStateToProps,
mapDispatchToProps,
mergeProps,
dispatch,
options
)
}
该函数相当于一个控制器,根据pure
选项来决定使用那种具体实现。
如果pure
为true
,则selectorFactory将会返回一个可以记忆结果的selector,这样才有可能使得connectHOC
里面的selector.shouldComponentUpdate
为false
。反之,selector将永远返回新的对象,selector.shouldComponentUpdate
始终为true
impureFinalPropsSelectorFactory
export function impureFinalPropsSelectorFactory(
mapStateToProps,
mapDispatchToProps,
mergeProps,
dispatch
) {
return function impureFinalPropsSelector(state, ownProps) {
return mergeProps(
mapStateToProps(state, ownProps),
mapDispatchToProps(dispatch, ownProps),
ownProps
)
}
}
直接就很粗暴了
pureFinalPropsSelectorFactory
export function pureFinalPropsSelectorFactory(
mapStateToProps,
mapDispatchToProps,
mergeProps,
dispatch,
{ areStatesEqual, areOwnPropsEqual, areStatePropsEqual }
) {
// 闭包,memory之前的结果,仅当有变化的时候才更新
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
return mergedProps
}
function handleNewPropsAndNewState() {
// state 已经 changed
stateProps = mapStateToProps(state, ownProps)
if (mapDispatchToProps.dependsOnOwnProps)
dispatchProps = mapDispatchToProps(dispatch, ownProps)
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
return mergedProps
}
function handleNewProps() {
// 仅当state 用到了OwnProps才更新
if (mapStateToProps.dependsOnOwnProps)
stateProps = mapStateToProps(state, ownProps)
// 仅当disaptch 用到了OwnProps才更新
if (mapDispatchToProps.dependsOnOwnProps)
dispatchProps = mapDispatchToProps(dispatch, ownProps)
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
return mergedProps
}
function handleNewState() {
const nextStateProps = mapStateToProps(state, ownProps)
// shallow compare state是否改变
const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps)
stateProps = nextStateProps
if (statePropsChanged)
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
return mergedProps
}
// 非首次调用
function handleSubsequentCalls(nextState, nextOwnProps) {
const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)
const stateChanged = !areStatesEqual(nextState, state)
state = nextState
ownProps = nextOwnProps
if (propsChanged && stateChanged) return handleNewPropsAndNewState()
if (propsChanged) return handleNewProps()
if (stateChanged) return handleNewState()
return mergedProps
}
return function pureFinalPropsSelector(nextState, nextOwnProps) {
return hasRunAtLeastOnce
? handleSubsequentCalls(nextState, nextOwnProps)
: handleFirstCall(nextState, nextOwnProps)
}
}
看上去复杂许多,但主要还是因为需要区分是否是首次调用而已。
mapStateToProps
mapStateToProps
和mapDispatchToProps
差不多,大部分都是公共代码(wrapMapToProps
),因此我们以mapStateToProps
为例:
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
]
跟selectorFactory一样,这里同样有多种解决方案,不过不同的是,这里是直接将所有方案以数组的方式返回,供前面connect
中的match
函数匹配。
然后我们主要来看一下其中涉及到的几个函数:
wrapMapToPropsConstant
export function wrapMapToPropsConstant(getConstant) {
return function initConstantSelector(dispatch, options) {
const constant = getConstant(dispatch, options)
function constantSelector() { return constant }
constantSelector.dependsOnOwnProps = false
return constantSelector
}
}
顾名思义,constant不允许结果发生变化的,所以函数里面调用传入的getConstant
函数之后,直接返回一个getter函数:contantSelector
wrapMapToPropsFunc
// methodName只用来debug
export function wrapMapToPropsFunc(mapToProps, methodName) {
// options 只需要 displayname , 也只用来debug
return function initProxySelector(dispatch, { displayName }) {
const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
return proxy.dependsOnOwnProps
? proxy.mapToProps(stateOrDispatch, ownProps)
: proxy.mapToProps(stateOrDispatch)
}
proxy.dependsOnOwnProps = true
proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {
proxy.mapToProps = mapToProps
proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
let props = proxy(stateOrDispatch, ownProps)
// 如果mapToProps执行后返回的结果还是一个函数,则继续递归调用,直到它扁平为止
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
}
}
这里主要运用了代理的方法,挺巧妙的对mapToProps
方法进行了包装,增加了dependsOnOwnProps
这个静态成员,判断方法很简单,直接判断Function的参数长度即可:
getDependsOnOwnProps
export function getDependsOnOwnProps(mapToProps) {
return (mapToProps.dependsOnOwnProps !== null && mapToProps.dependsOnOwnProps !== undefined)
? Boolean(mapToProps.dependsOnOwnProps)
: mapToProps.length !== 1
}
至此,所有代码都通了, cheers!~