此文档用于自己方便记住react-redux和react的流程,没有仔细的研究,可能有错误。
以官方示例来探究:
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
TodoList from '../components/TodoList'
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
case 'SHOW_ALL':
default:
return todos
}
}
const mapStateToProps = state => ({
todos: getVisibleTodos(state.todos, state.visibilityFilter)
})
const mapDispatchToProps = dispatch => ({
toggleTodo: id => dispatch(toggleTodo(id))
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
connect是怎么工作的?首先我们进入master/src/connect/connect.js,可以看出connect就是
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
})
首先initMapStateToProps是什么呢?进入connect/mapStateToProps.js,我们发现match的结果相当于执行了wrapMapToPropsFunc(mapStateToProps, ‘mapStateToProps’)这个函数,进入wrapMapToProps.js中,发现wrapMapToPropsFunc()函数返回initProxySelector这个函数,这时我们可以认为initMapStateToProps就是initProxySelector这个函数。这个函数最终在selectorFactory.js中的 const mapStateToProps = initMapStateToProps(dispatch, options)执行,返回proxy函数对象和其方法。
proxy.mapToProps方法的参数由connect/selectorFactory.js中的mapStateToProps(state, ownProps),mapDispatchToProps(dispatch, ownProps)中决定,这就是mapStateToProps函数和mapDispatchToProps的参数由来
//此处的mapToProps实际就是connect(mapStateToProps,mapDispatchToProps)的mapStateToProps
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
) {
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
}
从上面代码可以看出connect( mapStateToProps,mapDispatchToProps)返回的结果就是connectHOC()这个函数执行的结果,那么这个函数是什么呢?顺着函数的调用,发现此函数就是components/connectAdvanced.js文件中的connectAdvanced函数
那么connect()(TodoList)相当于wrapWithConnect(TodoList);
接下我们看看wrapWithConnect(TodoList)这个函数做了什么
此函数返回了一个class Connect ,这也验证官网说的connect是返回高阶组件的高阶函数。
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
)
this.selectDerivedProps = makeDerivedPropsSelector()
this.selectChildElement = makeChildElementSelector()
this.indirectRenderWrappedComponent = this.indirectRenderWrappedComponent.bind(
this
)
}
indirectRenderWrappedComponent(value) {
// calling renderWrappedComponent on prototype from indirectRenderWrappedComponent bound to `this`
return this.renderWrappedComponent(value)
}
//此处构建根据传入的函数构建组件
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.`
)
//此处的storeState,store是Provider组件中的store属性提供的,Provider是最上层的根
const { storeState, store } = value
let wrapperProps = this.props
let forwardedRef
if (forwardRef) {
wrapperProps = this.props.wrapperProps
forwardedRef = this.props.forwardedRef
}
/*const selectorFactoryOptions = {
...connectOptions,
getDisplayName,
methodName,
renderCountProp,
shouldHandleStateChanges,
storeKey,
displayName,
wrappedComponentName,
WrappedComponent
}*/
//selectorFactoryOptions 中connectOptions 包含initMapStateToProps和initMapDispatchToProps
let derivedProps = this.selectDerivedProps(
storeState,
wrapperProps,
store,
selectorFactoryOptions
)
// childProps为derivedProps
return this.selectChildElement(
WrappedComponent,
derivedProps,
forwardedRef
)
}
render() {
//此处为react.createContext()创建的Context ,Content必须接收一个函数作为子节点,value为provider的提供的值
const ContextToUse =
this.props.context &&
this.props.context.Consumer &&
isContextConsumer( )
? this.props.context
: Context
return (
{this.indirectRenderWrappedComponent}
)
}
}
Connect.WrappedComponent = WrappedComponent
Connect.displayName = displayName
if (forwardRef) {
const forwarded = React.forwardRef(function forwardConnectRef(
props,
ref
) {
return
})
forwarded.displayName = displayName
forwarded.WrappedComponent = WrappedComponent
return hoistStatics(forwarded, WrappedComponent)
}
return hoistStatics(Connect, WrappedComponent)
}
下面看看高阶组件注入的childProps有哪些东西呢?追溯上一级
let derivedProps = this.selectDerivedProps(
storeState,
wrapperProps,
store,
selectorFactoryOptions
)
this.selectDerivedProps函数实际是下面这个函数
//state为Provider的getState store为store
return function selectDerivedProps(
state,
props,
store,
selectorFactoryOptions
) {
if (pure && lastProps === props && lastState === state) {
return lastDerivedProps
}
if (
store !== lastStore ||
lastSelectorFactoryOptions !== selectorFactoryOptions
) {
lastStore = store
lastSelectorFactoryOptions = selectorFactoryOptions
//等同于impureFinalPropsSelector函数
sourceSelector = selectorFactory(
store.dispatch,
selectorFactoryOptions
)
}
lastProps = props
lastState = state
const nextProps = sourceSelector(state, props)
lastDerivedProps = nextProps
return lastDerivedProps
}
其中selectorFactory是connect/selectorFactory.js中的finalPropsSelectorFactory函数,如下
export default function finalPropsSelectorFactory(
dispatch,
{ initMapStateToProps, initMapDispatchToProps, initMergeProps, ...options }
) {
//initMapStateToProps为initProxySelector
const mapStateToProps = initMapStateToProps(dispatch, options) //返回proxy函数
const mapDispatchToProps = initMapDispatchToProps(dispatch, options) //返回proxy函数
//initMergeProps这个
const mergeProps = initMergeProps(dispatch, options)
if (process.env.NODE_ENV !== 'production') {
verifySubselectors(
mapStateToProps,
mapDispatchToProps,
mergeProps,
options.displayName
)
}
const selectorFactory = options.pure
? pureFinalPropsSelectorFactory
: impureFinalPropsSelectorFactory
//此处的selectorFactory为pureFinalPropsSelectorFactory函数,(pure开始传入为true)不要混淆
return selectorFactory(
mapStateToProps,
mapDispatchToProps,
mergeProps,
dispatch,
options
)
}
其中mapStateToProps通过上面可知相当于initProxySelector函数,执行后返回proxy函数,在impureFinalPropsSelectorFactory函数中执行返回connect( mapStateToProps,mapDispatchToProps)这个函数参数mapStateToProps中返回的对象,mapDispatchToProps没研究,猜测同理。注意,如果不传入mapDispatchToProps则会返回个dispatch方法
mergeProps由initMergeProps(dispatch, options)而来,假如connect不传第三个mergeProps参数,则由
export function whenMergePropsIsOmitted(mergeProps) {
return !mergeProps ? () => defaultMergeProps : undefined
}
可知initMergeProps是一个箭头函数() => defaultMergeProps,传入dispatch, options这两个参数以后闭包备用,执行后返回一个defaultMergeProps函数,这时mergeProps 相当于defaultMergeProps函数。
pureFinalPropsSelectorFactory函数执行后返回pureFinalPropsSelector(nextState, nextOwnProps) {}函数,也就是selectorFactory执行后也就是sourceSelector等同于pureFinalPropsSelector(state, ownProps) {}这个函数。
那么nextProps结果就是handleFirstCall函数返回mergeProps(stateProps, dispatchProps, ownProps)的结果,组件传递给WrappedComponent的props也就是nextProps了,这个props带有mapStateToProps执行后的对象,mapDispatchToProps执行后的对象,如果不传则传入dispatch对象和自己的属性
总结:
(1)connect是通过react.creactContext方法用来连接组件和Provider提供的value(也就是store和store.getState;
(2)connect把mapStateToProps和mapDispatchToProps返回的对象及本身的props传入转换组件的props中,mapDispatchToProps不传入时会返回dispatch方法注入props。注意如果mapDispatchToProps定义了事件触发最好在mapDispatchToProps中写,props中没有dispatch方法。