react-redux源码学习笔记

前端之家收集整理的这篇文章主要介绍了react-redux源码学习笔记前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

###react-redux源码结构

-- index.js

-- utils - shallowEqual.js - storeShape.js - warning.js - wrapActionCreators.js

--components - Provider.js - connect.js

为了方便解读,先从基础方法开始

index.js 源码

import Provider from './components/Provider' // 导入Provider组件
import connect from './components/connect'  // 导入connect组件
export { Provider,connect }               // 将Provider,connect导出

utils 工具包源码

warning.js

/**
 * 
 * 在console打印一条警告信息
 *
 * @param {String} 警告信息内容
 * @returns {void} 返回值
 */
export default function warning(message) {
  
  // 文件中指定规则(console)不验证
  /* eslint-disable no-console */
  if (typeof console !== 'undefined' && typeof console.error === 'function') {
    console.error(message)
  }
  /* eslint-enable no-console */

  try {

    // 抛出异常并暂停执行
    throw new Error(message)
    
  /* eslint-disable no-empty */
  } catch (e) {} 
  /* eslint-enable no-empty */
 
}

shallowEqual.js

// 判断objA 与 obj 是否相等
export default function shallowEqual(objA,objB) {
  
  // 如果objA和objB是严格相等,结果相等
  if (objA === objB) {
    return true
  }
  
  // 获取 objA 和 objB 的所有属性
  const keysA = Object.keys(objA)
  const keysB = Object.keys(objB)
   
  // 属性长度不相等,结果不相等
  if (keysA.length !== keysB.length) {
    return false
  }

  // 使用hasWon引用Object.prototype.hasOwnProperty方法
  const hasOwn = Object.prototype.hasOwnProperty
 
  // objB 中不含有 objA 中的属性,结果不相等
  for (let i = 0; i < keysA.length; i++) {
    if (!hasOwn.call(objB,keysA[i]) ||
        objA[keysA[i]] !== objB[keysA[i]]) {
      return false
    }
  }

  return true
}

storeShape.js

import { PropTypes } from 'react'

// 导出store Prop 验证规则
export default PropTypes.shape({
  subscribe: PropTypes.func.isrequired,dispatch: PropTypes.func.isrequired,getState: PropTypes.func.isrequired
})

wrapActionCreators.js

// 导入 bindActionCreators 方法,此方法在后续redux源码会讲到
import { bindActionCreators } from 'redux'

// 将dispatch应用到actionCreators
export default function wrapActionCreators(actionCreators) {
  return dispatch => bindActionCreators(actionCreators,dispatch)
}

components源码

Provider.js

// Provider 依赖react,导入react中元素
import { Component,PropTypes,Children } from 'react'
// 导入store验证规则以及警告提示方法
import storeShape from '../utils/storeShape'
import warning from '../utils/warning'

// 标识是否已经收到警告
let didWarnAboutReceivingStore = false
// 此方法主要用来警告当前的对象是否和传入的对象相同
function warnAboutReceivingStore() {
  if (didWarnAboutReceivingStore) {
    return
  }
  didWarnAboutReceivingStore = true

  warning(
    '<Provider> does not support changing `store` on the fly. ' +
    'It is most likely that you see this error because you updated to ' +
    'Redux 2.x and React Redux 2.x which no longer hot reload reducers ' +
    'automatically. See https://github.com/reactjs/react-redux/releases/' +
    'tag/v2.0.0 for the migration instructions.'
  )
}

// Provider是一个内部组建
export default class Provider extends Component {
  
  // 构造方法传入props和context
  constructor(props,context) {
    super(props,context)
    this.store = props.store
  }

  // 返回从构造方法传递的store,将store传递给子孙component
  getChildContext() {
    return { store: this.store }
  }
  
  // 返回仅有的一个子元素,否则(没有子元素或超过一个子元素)
  // 报错且不渲染任何东西。 这也说明Provider下必须只能是一个
  // 子元素
  render() {
    return Children.only(this.props.children)
  }
}

// 如果我们运行的环境是production,则还需要定义
// componentWillReceiveProps原型方法
if (process.env.NODE_ENV !== 'production') {
  Provider.prototype.componentWillReceiveProps = function (nextProps) {
    const { store } = this
    const { store: nextStore } = nextProps
    // 如果当前对象与传递的对象不一样,则发出警告
    if (store !== nextStore) {
      warnAboutReceivingStore()
    }
  }
}

Provider.propTypes = {
  store: storeShape.isrequired,children: PropTypes.element.isrequired
}
Provider.childContextTypes = {
  store: storeShape.isrequired
}

connect.js react-redux真正的主角上场了,游戏才刚开始,游戏结束还有59分钟。

// 这个就是导入一系列react、utils中的方法
import { Component,createElement } from 'react'
import storeShape from '../utils/storeShape'
import shallowEqual from '../utils/shallowEqual'
import wrapActionCreators from '../utils/wrapActionCreators'
import warning from '../utils/warning'
import isPlainObject from 'lodash/isPlainObject'
import hoistStatics from 'hoist-non-react-statics'
import invariant from 'invariant'

// 一看default,嘿,connect方法中mapStateToProps、mapDispatchToProps的默认值。
// mapStateToProps默认为一个空对象
// mapDispatchToProps默认为包含一个dispatch方法的对象
// mergeProps主要是合并上两个和其他
const defaultMapStateToProps = state => ({}) // eslint-disable-line no-unused-vars
const defaultMapDispatchToProps = dispatch => ({ dispatch })
const defaultMergeProps = (stateProps,dispatchProps,parentProps) => ({
  ...parentProps,...stateProps,...dispatchProps
})

// 显示组件名称
function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

// 运行指定方法并捕获异常,并将错误放在errorObject对象中的value中
let errorObject = { value: null }
function tryCatch(fn,ctx) {
  try {
    return fn.apply(ctx)
  } catch (e) {
    errorObject.value = e
    return errorObject
  }
}

// Helps track hot reloading.
let nextVersion = 0

// connect是一个高阶函数,它包含四个参数,并返回一个
// 生产Component的函数,它将真正的Component作为参数传入
export default function connect(mapStateToProps,mapDispatchToProps,mergeProps,options = {}) {
  
  // mapStateToProps第一个参数(function)
  // 返回bool值,使用传入mapStateToProps判断是否可订阅
  const shouldSubscribe = Boolean(mapStateToProps)
  //mapStateToProps默认值,即{}
  const mapState = mapStateToProps || defaultMapStateToProps

  let mapDispatch
  
  // mapDispatchToProps为第二个参数(function | object),
  // mapDispatchToProps可传递的类型为function 或许 对象
   
  // 如果传递的类型为function 
  if (typeof mapDispatchToProps === 'function') {
    mapDispatch = mapDispatchToProps
   
  // 如果没有传递mapDispatchToProps,则为默认
  } else if (!mapDispatchToProps) { 
    mapDispatch = defaultMapDispatchToProps
  } else {
    // 只要敢调用wrapActionCreators,它就返回参数含有dispatch的方法。
    // wrapActionCreators方法中应用了bindActionCreators
    mapDispatch = wrapActionCreators(mapDispatchToProps)
  }

  // 第三个参数(function), 如果指定这个参数
  // mapStateToProps() 与 mapDispatchToProps()的
  // 执行结果和组件自身的 props 将传入到这个回调函数中
  const finalMergeProps = mergeProps || defaultMergeProps
  
  // 第四个参数(Object)
  // pure为true表示执行shouldComponentUpdate并且浅比较mergeProps
  // withRef为true表示connector会保存一个对被包装组件实例的引用,
  // 该引用通过 getWrappedInstance() 方法获得
  const { pure = true,withRef = false } = options

  // 判断是否检查 mergeProps
  const checkMergedEquals = pure && finalMergeProps !== defaultMergeProps

  // 保持版本号增长(非生产环境下).
  const version = nextVersion++

  // connect的返回值,WrappedComponent是React中的一个容器组件
  return function wrapWithConnect(WrappedComponent) {
    // 格式化顶级组件的名称
    const connectDisplayName = `Connect(${getDisplayName(WrappedComponent)})`

    // 校验State中的props 是否为纯对象
    function checkStateShape(props,methodName) {
      // 判断对象否为纯粹的对象字面量,即用来检测一个对象是否由{}或new Object()创建 
      if (!isPlainObject(props)) {
        warning(
          `${methodName}() in ${connectDisplayName} must return a plain object. ` +
          `Instead received ${props}.`
        )
      }
    }

    // 合并stateProps、dispatchProps、parentProps 
    function computeMergedProps(stateProps,parentProps) {
      const mergedProps = finalMergeProps(stateProps,parentProps)
      
      // 不为生产环境时才执行
      if (process.env.NODE_ENV !== 'production') {
        checkStateShape(mergedProps,'mergeProps')
      }
      return mergedProps
    }


    // 定义Connect组件
    class Connect extends Component {
      
      // 在接收到新的 props 或者 state,将要渲染之前调用。
      // 该方法在初始化渲染的时候不会调用
      // 如果 shouldComponentUpdate 返回 false,则 render() 将不会执行
      shouldComponentUpdate() {
        return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged
      }

      // 构造方法
      constructor(props,context) {
        super(props,context)
        this.version = version
        
        //从祖先Component处获得store
        this.store = props.store || context.store
         
         // props 或 context没有存放store,invariant(this.store,`Could not find "store" in either the context or ` +
          `props of "${connectDisplayName}". ` +
          `Either wrap the root component in a <Provider>,` +
          `or explicitly pass "store" as a prop to "${connectDisplayName}".`
        )
         
        // 从store获取state并成为当前对象的state
        const storeState = this.store.getState()
        this.state = { storeState }
        this.clearCache()
      }

      
      computeStateProps(store,props) {

        // 如果finalMapStateToProps不存在,表示还没有执行过configureFinalMapState方法
        if (!this.finalMapStateToProps) {
          return this.configureFinalMapState(store,props)
        }
        
         // 获取store中的state, 并执行finalMapStateToProps
        // 如果StateProps依赖OwnProps,需要传递props
        const state = store.getState()
        const stateProps = this.doStatePropsDependOnOwnProps ?
          this.finalMapStateToProps(state,props) :
          this.finalMapStateToProps(state)

        if (process.env.NODE_ENV !== 'production') {
          checkStateShape(stateProps,'mapStateToProps')
        }
        return stateProps
      }

      // 1. 调用mapState,即connect第一个参数,返回包含
      // 2. 判断是否为 function
      // 3. 判断ffinalMapStateToProps参数个数,长度为1,代表不依赖ownProps,否则反之
      configureFinalMapState(store,props) {
        const mappedState = mapState(store.getState(),props)
        const isFactory = typeof mappedState === 'function'

        this.finalMapStateToProps = isFactory ? mappedState : mapState
        this.doStatePropsDependOnOwnProps = this.finalMapStateToProps.length !== 1

        if (isFactory) {
          return this.computeStateProps(store,props)
        }

        if (process.env.NODE_ENV !== 'production') {
          checkStateShape(mappedState,'mapStateToProps')
        }
        return mappedState
      }

      computeDispatchProps(store,props) {
        // 如果没有调用过configureFinalMapDispatch, 则先调
        // 用configureFinalMapDispatch
        if (!this.finalMapDispatchToProps) {
          return this.configureFinalMapDispatch(store,props)
        }

        // 获取store中的dispatch, 并执行finalMapDispatchToProps,返回值为对象
        // 如果DispatchProps依赖OwnProps,需要传递props
        const { dispatch } = store
        const dispatchProps = this.doDispatchPropsDependOnOwnProps ?
          this.finalMapDispatchToProps(dispatch,props) :
          this.finalMapDispatchToProps(dispatch)

        if (process.env.NODE_ENV !== 'production') {
          checkStateShape(dispatchProps,'mapDispatchToProps')
        }
        
        // 返回最终dispatchProps
        return dispatchProps
      }

      configureFinalMapDispatch(store,props) {
        // 调用mapDispatch()方法, mapDisPatch方法返回一个含有dispatch[也可能含有props]参数的function
        const mappedDispatch = mapDispatch(store.dispatch,props)
        // 如果返回的是函数,isFactory为true。
        const isFactory = typeof mappedDispatch === 'function'

        // isFactory为true,返回mappedDispatch,否则返回默认值
        this.finalMapDispatchToProps = isFactory ? mappedDispatch : mapDispatch
        // 判断finalMapDispatchToProps参数个数,长度为1,代表不依赖ownProps,否则反之
        this.doDispatchPropsDependOnOwnProps = this.finalMapDispatchToProps.length !== 1
        
        // mappedDispatch === 'function'成立,调用computeDispatchProps
        if (isFactory) {
          return this.computeDispatchProps(store,props)
        }

        if (process.env.NODE_ENV !== 'production') {
          checkStateShape(mappedDispatch,'mapDispatchToProps')
        }
        return mappedDispatch
      }

      // 是否需要更新StateProps
      updateStatePropsIfNeeded() {
        const nextStateProps = this.computeStateProps(this.store,this.props)
        if (this.stateProps && shallowEqual(nextStateProps,this.stateProps)) {
          return false
        }

        this.stateProps = nextStateProps
        return true
      }
      
      // 是否需要更新DispatchProps
      updateDispatchPropsIfNeeded() {
        const nextDispatchProps = this.computeDispatchProps(this.store,this.props)
        if (this.dispatchProps && shallowEqual(nextDispatchProps,this.dispatchProps)) {
          return false
        }

        this.dispatchProps = nextDispatchProps
        return true
      }

      // 是否需要更新MergeProps
      updateMergedPropsIfNeeded() {
        const nextMergedProps = computeMergedProps(this.stateProps,this.dispatchProps,this.props)
        if (this.mergedProps && checkMergedEquals && shallowEqual(nextMergedProps,this.mergedProps)) {
          return false
        }

        this.mergedProps = nextMergedProps
        return true
      }

      // 是否已经被订阅订阅返回true
      isSubscribed() {
        return typeof this.unsubscribe === 'function'
      }

      //  执行订阅,并使用handleChange处理回调
      //  先自动调用handleChange一次
      trySubscribe() {
        if (shouldSubscribe && !this.unsubscribe) {
          this.unsubscribe = this.store.subscribe(this.handleChange.bind(this))
          this.handleChange()
        }
      }

      // 取消订阅
      tryUnsubscribe() {
        if (this.unsubscribe) {
          this.unsubscribe()
          this.unsubscribe = null
        }
      }

      // 初始化渲染执行之后立刻调用一次
      // 渲染之后执行订阅
      componentDidMount() {
        this.trySubscribe()
      }

      // 组件接收到新的 props 的时候调用。在初始化渲染的时候,该方法不会调用。
      //  判断传递的Props是否与当前props相等
      componentWillReceiveProps(nextProps) {
        if (!pure || !shallowEqual(nextProps,this.props)) {
          this.haveOwnPropsChanged = true
        }
      }

      // 在组件从 DOM 中移除的时候立刻被调用
      // 取消订阅 、清除缓存
      componentWillUnmount() {
        this.tryUnsubscribe()
        this.clearCache()
      }

      // reset变量及引用
      clearCache() {
        this.dispatchProps = null
        this.stateProps = null
        this.mergedProps = null
        this.haveOwnPropsChanged = true
        this.hasStoreStateChanged = true
        this.haveStatePropsBeenPrecalculated = false
        this.statePropsPrecalculationError = null
        this.renderedElement = null
        this.finalMapDispatchToProps = null
        this.finalMapStateToProps = null
      }

      // handle回调实现
      handleChange() {
        
        // 如果被取消订阅,终止执行。
        if (!this.unsubscribe) {
          return
        }

        // 比较当前state是否store中的state相同
        // 相同停止执行
        const storeState = this.store.getState()
        const prevStoreState = this.state.storeState
        if (pure && prevStoreState === storeState) {
          return
        }

        if (pure && !this.doStatePropsDependOnOwnProps) {
          const haveStatePropsChanged = tryCatch(this.updateStatePropsIfNeeded,this)
          if (!haveStatePropsChanged) {
            return
          }
          if (haveStatePropsChanged === errorObject) {
            this.statePropsPrecalculationError = errorObject.value
          }
          this.haveStatePropsBeenPrecalculated = true
        }

        // 重新设置state
        this.hasStoreStateChanged = true
        this.setState({ storeState })
      }

      // 获取被包装组件实例的引用
      // 如果没有设置withRef为true,
      // 执行invariant
      getWrappedInstance() 
        invariant(withRef,`To access the wrapped instance,you need to specify ` +
          `{ withRef: true } as the fourth argument of the connect() call.`
        )

        return this.refs.wrappedInstance
      }

      render() {
        // 解构所需变量
        const {
          haveOwnPropsChanged,hasStoreStateChanged,haveStatePropsBeenPrecalculated,statePropsPrecalculationError,renderedElement
        } = this

        // reset变量 
        this.haveOwnPropsChanged = false
        this.hasStoreStateChanged = false
        this.haveStatePropsBeenPrecalculated = false
        this.statePropsPrecalculationError = null
        
        // 如果有错误存在statePropsPrecalculationError中,抛出错误
        if (statePropsPrecalculationError) {
          throw statePropsPrecalculationError
        }

     
        let shouldUpdateStateProps = true
        let shouldUpdateDispatchProps = true

        // 允许浅比较并且renderdElement存在,说明只需刷新
        if (pure && renderedElement) {
          // 判断是否需要更新state
          shouldUpdateStateProps = hasStoreStateChanged || (
            haveOwnPropsChanged && this.doStatePropsDependOnOwnProps
          )
          // 判断是否需要更新dispatch
          shouldUpdateDispatchProps =
            haveOwnPropsChanged && this.doDispatchPropsDependOnOwnProps
        }

        let haveStatePropsChanged = false
        let haveDispatchPropsChanged = false
      
         
        if (haveStatePropsBeenPrecalculated) {
          haveStatePropsChanged = true
        } else if (shouldUpdateStateProps) {
          haveStatePropsChanged = this.updateStatePropsIfNeeded()
        }

        if (shouldUpdateDispatchProps) {
          haveDispatchPropsChanged = this.updateDispatchPropsIfNeeded()
        }

        let haveMergedPropsChanged = true
        
        // 如果haveStatePropsChanged、haveDispatchPropsChanged、
        // haveOwnPropsChanged其中一个被改变
        if (
          haveStatePropsChanged ||
          haveDispatchPropsChanged ||
          haveOwnPropsChanged
        ) {
          haveMergedPropsChanged = this.updateMergedPropsIfNeeded()
        } else {
          haveMergedPropsChanged = false
        }

        if (!haveMergedPropsChanged && renderedElement) {
          return renderedElement
        }

        // 如果withRef为true,则保存一个对被包装组件实例的引用:
        // ref: 'wrappedInstance' 是此组建创建的引用
 
        if (withRef) {
          this.renderedElement = createElement(WrappedComponent,{
            ...this.mergedProps,ref: 'wrappedInstance'
          })
        } else {
          this.renderedElement = createElement(WrappedComponent,this.mergedProps
          )
        }

        return this.renderedElement
      }
    }

    Connect.displayName = connectDisplayName
    Connect.WrappedComponent = WrappedComponent
    Connect.contextTypes = {
      store: storeShape
    }
    Connect.propTypes = {
      store: storeShape
    }

    if (process.env.NODE_ENV !== 'production') {
      Connect.prototype.componentWillUpdate = function componentWillUpdate() {
        if (this.version === version) {
          return
        }

        // We are hot reloading!
        this.version = version
        this.trySubscribe()
        this.clearCache()
      }
    }

    // 这才是衔接redux 与 react 的关键, 将 WrappedComponent 中
    // 除去关键字的所有的key和Symbols赋给 Connect (react 和 js关键字)
    // [hoist-non-react-statics源码](https://github.com/mridgway/hoist-non-react-statics/blob/master/index.js)
    return hoistStatics(Connect,WrappedComponent)
  }
}

参考: http://zhenhua-lee.github.io/react/redux.html

原文链接:https://www.f2er.com/react/306459.html

猜你在找的React相关文章