React-redux中connect方法探究

React-redux中connect方法探究

此文档用于自己方便记住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方法。

你可能感兴趣的:(React)