React-redux source code parsing

React-redux is a link library for large React applications linking react and Redux, and is widely used in many REAT projects. As developers, students understand the principles and source code of Reac-Redux, which will help us improve our ability.

Preliminary knowledge

react context

React Advanced Component (HOC)

React-redux = connectAdvanced API = connectAdvanced API = connectAdvanced API = connectAdvanced API

The preparatory work

Old rules github find React-Redux, Clone Master

Version of this article: 6.0.0-beta.3Copy the code

You can also check out my gitHub project, which contains the source code and detailed comments for redux and React-Redux

Began to lu code

After you open the project, let’s go straight to the core code in SRC, starting with index.js (oh, react-redux is based on react and redux, check out peerDependencies in package.json).

As always, the exposed Provider, connectAdvanced, and Connect have apis. Let’s take a look at the Provider implementation first.

The Provider implementation

Let’s think about what reac-redux does. It simply wraps the root component with < Provider> < /Provider> and passes the store as props to each component wrapped with connect(). The calculation of state to props is optimized. React itself supports the passing of props from top to bottom, but also supports the passing of context across the hierarchy

PropTypes provides props -> constructor -> render -> declare periodic hook methods -> summary

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { ReactReduxContext } from './Context'

class Provider extends Component {
  constructor(props) {
    super(props)

    / / for the store
    const { store } = props

    // Initialize state. StoreState is the initial redux state
    this.state = {
      storeState: store.getState(),
      // Save init Store
      store
    }
  }

  componentDidMount() {
    // Set "componentWillUnmount" to false. // Set "componentWillUnmount" to false
    ComponentDidMount -> componentWillUnmount componentDidMount -> componentWillUnmount
    this._isMounted = true
    // Let's look at subscribe
    this.subscribe()
  }

  componentWillUnmount() {
    // Unsubscribe
    if (this.unsubscribe) this.unsubscribe()
    If this._isMounted is set to false =>_isMounted The assumption is true βœ…
    this._isMounted = false
  }

  componentDidUpdate(prevProps) {
    // If the reference to the store changes after the update
    if (this.props.store ! == prevProps.store) {// Cancel the listener if it exists
      if (this.unsubscribe) this.unsubscribe()
      / / update the storeState
      this.subscribe()
    }
    /* * the store of redux should be fixed in order to provide the "global variable state" * since the author of redux wrote this, Let store = createStore(rootReducer, initState, applyMiddleware(XXX)); * this.props.store ! == prevProps. Store Indicates that the global store has changed. If the createStore method is called again, * those of you who have read the redux source code should know that the "global state" will change now that it is a new createStore, Instead of the previous memory space * so here we call subscribe again to update the storeState * * * */ in state

  }

  // Use the store.subscribe method to keep storeState up to date
  subscribe() {
    const { store } = this.props
    / / to monitor the subscribe
    this.unsubscribe = store.subscribe((a)= > {
     // Get the latest state and assign it to newStoreState
      const newStoreState = store.getState()
      // Not in this lifecycle return
      if (!this._isMounted) {
        return
      }

      this.setState(providerState= > {
        // If the value is the same, skip the unnecessary state update.
        // If state is the same reference, skip the update of state
        if (providerState.storeState === newStoreState) {
          return null
        }

        // Update the current storeState
        return { storeState: newStoreState }
      })
    })
    
    const postMountStoreState = store.getState()
    if(postMountStoreState ! = =this.state.storeState) {
      this.setState({ storeState: postMountStoreState })
    }
  }

  render() {
    // ReactReduxContext is the default context. Look at the -> context.js file and the createContext parameter is null
    const Context = this.props.context || ReactReduxContext

    / / the value for this. The state
    return (
      <Context.Provider value={this.state}>
        {this.props.children}
      </Context.Provider>}} provider.proptypes = {// accept store as props and specify the necessary methods in the Store object. Redux createStore returns the object store: PropTypes.shape({ subscribe: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired, getState: PropTypes. Func. IsRequired}), / / receive custom context context: PropTypes. Object, / / children, usually for the root container<app />Children: PropTypes. Any} export default Provider /** * Provider Summary ** Provider is a gateway component of the React application<Provider store={store}><App /></Provider>* Use the React Context to pass stores. Older versions of Provider used getChildContext. With the React Context API changes, new producer-consumer apis are emerging. The context provided is {store, storeState: state}, and state is saved to the latest * * * */Copy the code

Context.js provides a global initialization of the ReactReduxContext

  import React from 'react'

/ / defaultValue is null
export const ReactReduxContext = React.createContext(null)

// Export to provide for providers and consumers
export default ReactReduxContext

Copy the code

Connect to realize

Let’s review how Connect can be used:

connect([mapStateToProps],[mapDispatchToProps],[mergeProps],[options])(<Xxx / >)
Copy the code

Also check out the connectAdvanced(selectorFactory, [connectOptions]) method, which is a function that connects the React component to the Redux Store. This function is the basis for connect().

Because connect method reference files, function and function nesting more, if you do not want to pay too much attention to details, you can directly look at the connect summary

Start by looking at the connect source code -> connect.js to see what the structure and references are -> then we start with export default -> see what functions are implemented when the function is called

connect.js

Connect ([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]) * mapDispatchToProps(dispatch, [ownProps]) * * React-redux ConnectAdvanced (selectorFactory, [connectOptions]) * Connects the React component to the Redux store. This parameter is the basis for connect */
import connectAdvanced from '.. /components/connectAdvanced'
import shallowEqual from '.. /utils/shallowEqual'
import defaultMapDispatchToPropsFactories from './mapDispatchToProps'
// mapStateToProps(state, ownProps)
// mapStateToProps is called whenever the Redux store changes,
// Or if a component with the ownProps parameter receives a new props,mapStateToProps will also be called
import defaultMapStateToPropsFactories from './mapStateToProps'
// The results of mapStateToProps() and mapDispatchToProps(), as well as the component's props, are passed into this callback function
import defaultMergePropsFactories from './mergeProps'
// Custom connector such as pure = true...
import defaultSelectorFactory from './selectorFactory'

/** ** : mapStatetoProps@param{*} mapStatetoProps *@param{x} factories array function, the function * default factory defaultMapStateToPropsFactories, [whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing] *@param {*} name  string
 * @returns  function
 */
function match(arg, factories, name) {
  // Run the factories from back to front
  for (let i = factories.length - 1; i >= 0; i--) {
    // Call the factories with the return value assigned to result, and see -> mapStateToProps
    const result = factories[i](arg)
    // Return result if result is true, result if (dispatch, options) => function(){... } the function
    if (result) return result
  }
  // Throw Error that does not conform to the connect method rules
  return (dispatch, options) => {
    throw new Error(
      `Invalid value of type ${typeof arg} for ${name} argument when connecting component ${
        options.wrappedComponentName
      }.`
    )
  }
}

// Check whether the indexes of the objects are the same
function strictEqual(a, b) {
  return a === b
}

// Expose the createConnect, which must return function because connect is a function
export function createConnectπŸ‘€ connectHOC = connectAdvanced, // connectAdvanced(selectorFactory, [connectOptions])
  mapStateToPropsFactories = defaultMapStateToPropsFactories, // array[function, function]
  mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
  mergePropsFactories = defaultMergePropsFactories,
  selectorFactory = defaultSelectorFactory
} = {}) {
  // Return connect function
  return function connectMapStateToProps, mapDispatchToProps, and mergeProps for connect. // Configure {pure =trueFunction areStatesEqual = strictEqual, πŸ‘‡ areOwnPropsEqual = shallowEqual, areStatePropsEqual = shallowEqual, areStatePropsEqual = shallowEqual, AreMergedPropsEqual = shallowEqual, // Other configuration items... extraOptions } = {} ) {
    // mapStateToProps is initialized
    const initMapStateToProps = match(
      // mapStateToProps function
      mapStateToProps,
      // Default -> Look at the match method first, then we'll look at mapStateToProps
      mapStateToPropsFactories,
      'mapStateToProps'
    )
    // Initialization of mapDispatchToProps
    const initMapDispatchToProps = match(
      mapDispatchToProps,
      mapDispatchToPropsFactories,
      'mapDispatchToProps'
    )
    // Initialize mergeProps
    const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')

    // Return connectHOC function to connect the React component to the Redux Store function
    // Let's look at the parameters
    // The selectorFactory function returns a selector function that calculates the new props from the store state, the display component props, and the dispatch. The new props are injected into the container component
    // selectorFactory -> defaultSelectorFactory
    // If you are familiar with the React-Redux API, you should be familiar with the connectHOC parameter, because it is the connectAdvanced method
    return connectHOC(selectorFactory, {
      // For misplacing information
      methodName: 'connect'.// Wrap getDisplayName with Connect
      getDisplayName: name => `Connect(${name})`,
      // If mapStateToProps is undefined, shouldHandleStateChanges is false, it does not listen to store state
      shouldHandleStateChanges: Boolean(mapStateToProps),

      // selectorFactory requires several parameters
      initMapStateToProps,     // (dispatch, options) => initProxySelector(dispatch, { displayName }){... }
      initMapDispatchToProps,
      initMergeProps,
      pure, / / true by default
      // strictEqual, it's easy to think of the strictEqual used to determine whether this. State is a reference
      areStatesEqual,  
      // shallowEqual Shallow comparison
      // React PureComponent is a component that can be used as a component. shallowEqual(instance.props, nextProps) || ! shallowEqual(instance.state, nextState)
      // If you are not familiar with it, look -> shallowequal.js
      areOwnPropsEqual,  
      areStatePropsEqual,
      areMergedPropsEqual,
      // Error tolerance, other configuration items. extraOptions }) } }// The connect method calls createConnect directly
export default createConnect(a)


Copy the code

mapStateToProps.js

import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'

// Call wrapMapToPropsFunc when mapStateToProps is function
export function whenMapStateToPropsIsFunction(mapStateToProps) {
  return typeof mapStateToProps === 'function'
    // Look at wrapMapToPropsFunc -> wrapMapToProps
    ? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps')
    : undefined
}

// The array in match is iterated backwards
// To determine whether to pass mapStateToProps
export function whenMapStateToPropsIsMissing(mapStateToProps) {
  // If mapStateToProps is passed and! MapStateToProps = true
  // mapStateToProps=false -> wrapMapToPropsConstant is not passed
  return! mapStateToProps ? wrapMapToPropsConstant(() => ({})) : undefined }// Export default is array and the element is function
export default [whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing]


/** * * * Call wrapMapToPropsFunc(mapstateToprops, 'mapStateToProps') * wrapMapToPropsConstant(() => ({})) * let's get to the root of this ->wrapMapToProps

Copy the code

wrapMapToProps.js

import verifyPlainObject from '.. /utils/verifyPlainObject'

/ * * * *@exportMapstateToprops calls the * for undefined@param {*} getConstant  () => ({})
 * @returns  function initConstantSelector(dispatch, options)
 */
export function wrapMapToPropsConstant(getConstant) {
  / / return initConstantSelector
  return function initConstantSelector(dispatch, options) {
    const constant = getConstant(dispatch, options) 

    function constantSelector(a) {
      return constant
    }
    constantSelector.dependsOnOwnProps = false
    return constantSelector
  }
}

// Used to determine whether ownProps exist
// mapStateToProps(state, [ownProps])
export function getDependsOnOwnProps(mapToProps) {
  // Not the first call returns Boolean directly
  returnmapToProps.dependsOnOwnProps ! = =null&& mapToProps.dependsOnOwnProps ! == undefined ? Boolean(mapToProps.dependsOnOwnProps)// mapToProps (dependsOnOwnProps) is undefined for the first time: mapToProps.length ! = =1  
}

/ * * *@exportCall * when mapstateToprops is passed@param{*} mapToProps uses connect@param{x} methodName name methodName = 'mapStatetoProps' | |' mapDispatchToProps' *@returnsReturn initProxySelector(Dispatch, {displayName}) */
export function wrapMapToPropsFunc(mapToProps, methodName) {
  // Finally found you!! Return initProxySelector function, which is assigned to initMapStateToProps(and initDispatchToProps).
  return function initProxySelector(dispatch, { displayName }) {
    // Define proxy function as return value
    const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
      return proxy.dependsOnOwnProps  // mapStateToProps Calculates the props of the dependent component
        ? proxy.mapToProps(stateOrDispatch, ownProps) // Return proxy.maptoprops. πŸ‘»
        : proxy.mapToProps(stateOrDispatch)
    }
    // dependsOnOwnProps to run the dependent component props to true
    proxy.dependsOnOwnProps = true

    // detectFactoryAndVerify for the returned function
    // The current call chain looks like this
    // const initMapStateToProps = initProxySelector(dispatch, { displayName })=>
    // mapToPropsProxy(stateOrDispatch, ownProps) => detectFactoryAndVerify(stateOrDispatch, ownProps)
    // detectFactoryAndVerify is assigned to proxy.maptoprops
    Return detectFactoryAndVerify(stateOrDispatch, ownProps) on the first call to mapToPropsProxy
    proxy.mapToProps = function detectFactoryAndVerify( stateOrDispatch, ownProps ) {
      MapToPropsfunction is assigned to proxy.maptoprops when called
      MapToProps = props; mapToProps = props; mapToProps = props
      proxy.mapToProps = mapToProps
      DependsOnOwnProps (true for the first time)
      proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
      // Props for proxy(stateOrDispatch, ownProps)
      First call initProxySelector() => proxy() =>
      // Proxy.maptoprops = detectFactoryAndVerify()
      // Call proxy(stateOrDispatch, ownProps) again with the passed mapToProps(... Args), the props we need for the React component
      let props = proxy(stateOrDispatch, ownProps)

      // Execute this function again if props is function
      if (typeof props === 'function') {
        proxy.mapToProps = props
        proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
        props = proxy(stateOrDispatch, ownProps)
      }

      // Non-Production environment check
      if(process.env.NODE_ENV ! = ='production')
        // verifyPlainObject is a utils method that throws a Warning if it is not a pure object
        verifyPlainObject(props, displayName, methodName)

      // Return the final props
      return props
    }

    return proxy
  }
}

Copy the code

Js and mapDisptchtoptoptoptop.js return array[function,function], and disptchToptoptoptop.js Return an initProxySelector function () => proxy => props

shallowEqual

ShallowEqual is used to make a shallow comparison of objects. It is often used in React applications, and should be used with shouldComponentUpdate for performance optimization. If you are familiar with PureComponent principle, you should know this code

! shallowEqual(instance.props, nextProps) || ! shallowEqual(instance.state, nextState)Copy the code

shallowEqual.js

// Whether the object itself has the specified property
const hasOwn = Object.prototype.hasOwnProperty

// Check whether the two values are the same
function is(x, y) {
   // SameValue algorithm
   if (x === y) { 
    returnx ! = =0|| y ! = =0 || 1 / x === 1 / y;
  } else {
    returnx ! == x && y ! == y; }}// A shallow comparison will only determine whether the ownProperty of two objects matches object. is, and will not recursively compare the ownProperty of two objects
// shallowEqual({x:{}},{x:{}}) // false
// shallowEqual({x:1},{x:1}) // true
export default function shallowEqual(objA, objB) {
  ShallowEqual returns Boolean if the same value is true
  if (is(objA, objB)) return true

  Return false if objA, objB is null or non-object if objA and objB are not the same value
  if( typeof objA ! = ='object' ||
    objA === null|| typeof objB ! = ='object' ||
    objB === null
  ) {
    return false
  }

  // Define the key array for objA, objB
  const keysA = Object.keys(objA)
  const keysB = Object.keys(objB)

  // The length of the object is different
  if(keysA.length ! == keysB.length)return false

  // Loop through keysA
  for (let i = 0; i < keysA.length; i++) {
    Return false if objB does not contain objA's key and objA is different from objB's value
    if(! hasOwn.call(objB, keysA[i]) || ! is(objA[keysA[i]], objB[keysA[i]])) {return false}}// If the for loop is passed, the first key and value in objA and objB are the same
  return true
}

Copy the code

Connect to summarize

Because there are too many ways to design the connect function, we will simplify the connect code here (the following is the source code simplified pseudo-code).

  // First inject some methods into connect in the form of parameters
export function createConnect({... // connectAdvanced for react-redux = connectAdvanced, connectHOC = connectAdvanced,... } = {}) {
  / / the connect method
  return function connectMapStateToProps, mapDispatchToProps, mergeProps, {pure =trueStrictEqual (a, b), // determine the object reference strictEqual(a, b)=> a === b
      shallowEqual, // The shallow comparison is described above. extraOptions// Other configuration items} = {}) {// A series of methods are executed, fault-tolerant for the three parameter types
    / / initialize the respective parameter mapStateToProps, mapDispatchToProps, mergeProps, injected some internal default parameters and methods
    // They look like this
    // (dispatch, options) => initProxySelector() => mapToPropsProxy() => props
    constinitMapStateToProps = match(... args)constinitMapDispatchToProps = match(... args)constinitMergeProps = match(... args)// The return value is obtained by executing connectAdvanced and passing in the initialized parameters such as initMapStateToProps and configuration items such as pure
    returnconnectAdvanced(selectorFactory, { initMapStateToProps, initMapDispatchToProps, initMergeProps, pure, ... extraOptions }) } }// Run the createConnect method directly to return connect
export default createConnect(a)
Copy the code

ConnectAdvanced Higher-order function

Connect ()(<A/ >) ΒΆ Connect ()(<A/ >) returns A high-level component, wrapping the current React component and returning an enhanced props component. ConnectAdvanced is executed when connect is executed. Next we have to look at the implementation of the connectAdvanced method, first introduced connectAdvanced -> πŸ‘€ to see the comments

connectAdvanced.js

/ / hoist - non - react - statics component, the component will automatically put all the binding on the object of the react to a static (static) methods are bound to the new object
import hoistStatics from 'hoist-non-react-statics'
// Info plugin
import invariant from 'invariant'
import React, { Component, PureComponent } from 'react'
import { isValidElementType } from 'react-is'
// context
import { ReactReduxContext } from './Context'

/ * * * *@exportIntroduce the basics of connectAdvanced * Connect (), the function that actually connects the React component to the Redux Store@param{*} selectorFactory function selectorFactory(dispatch, FactoryOptions) * initializes the selector function that is called when the Connector component needs to recalculate a new props as a result of the store's state change or when it receives a new props. The result of the selector should be a normal object, * passed as the props of the wrapped component. If successive calls to selector return the same object as the last call (===), * does not rerender the component. The selector's responsibility is to return the previous object when appropriate. *@param {*} [{
 *     getDisplayName = name => `ConnectAdvanced(${name})`,  DisplayName
 *     methodName = 'connectAdvanced',
 *     renderCountProp = undefined,
 *     shouldHandleStateChanges = true,
 *     storeKey = 'store',
 *     withRef = false,
 *     context = ReactReduxContext,
 *     ...connectOptions
 *   }={}]
 * @returns* /
export default function connectAdvancedSelectorFactory (selectorFactory, selectorFactory, selectorFactory, selectorFactory, selectorFactory); {// DisplayName attribute of the wrapped component getDisplayName = name => 'ConnectAdvanced(${name})`,
    // To display error messages
    methodName = 'connectAdvanced'.// Whether the component subscribs to the Redux Store's state changes
    shouldHandleStateChanges = true,
    renderCountProp = undefined, // The props key passed to the internal component, indicating the number of times the render method has been called
    // We can get props/context key for the store
    storeKey = 'store'.// If true, a reference is stored in the wrapped component instance and made available through the getWrappedInstance() method
    withRef = false.ForwardRef (); forwardRef(); forwardRef();
    forwardRef = false.// Provider ReactReduxContext methodcontext = ReactReduxContext, ... connectOptions } = {} ) {// invariant an error only in the development environment
  // When process.env.NODE_ENV is not production, the message is required.
  // These parameters are not used when warnning, skip this part directlyinvariant( renderCountProp === undefined, `renderCountProp is removed. render counting is built into the latest React dev tools profiling extension` ) invariant( ! withRef,'withRef is removed. To access the wrapped instance, use a ref on the connected component'
  )

  const customStoreWarningMessage =
    'To use a custom Redux store for specific components, create a custom React context with ' +
    "React.createContext(), and pass the context object to React-Redux's Provider and specific components" +
    ' like: 
      . ' +
    'You may also pass a {context : MyContext} option to connect'

  invariant(
    storeKey === 'store'.'storeKey has been removed and does not do anything. ' +
      customStoreWarningMessage
  )

  / / defines the Context
  const Context = context

  // Return react high-level component. WrappedComponent is the wrapped React component
  return function wrapWithConnect(WrappedComponent) {
    // Parameter test
    if(process.env.NODE_ENV ! = ='production') {
      invariant(
        isValidElementType(WrappedComponent),
        `You must pass a component to the function returned by ` +
          `${methodName}. Instead received ${JSON.stringify(WrappedComponent)}`
      )
    }

    // displayName for the Component, default Component
    const wrappedComponentName =
      WrappedComponent.displayName || WrappedComponent.name || 'Component'

    // displayName
    const displayName = getDisplayName(wrappedComponentName)

    // Define a selectorFactoryOptions object that contains all parameters for connect and connectAdvanced
    const selectorFactoryOptions = {
      / / connectOptions initMapStateToProps initMapDispatchToProps, parameters such as pure. connectOptions, getDisplayName, methodName, renderCountProp, shouldHandleStateChanges, storeKey, displayName, wrappedComponentName, WrappedComponent }// Pure determines whether to shwoEqual shouldComponentUpdate
    const { pure } = connectOptions

    // Component is assigned to OuterBaseComponent, using React high-level inheritance
    let OuterBaseComponent = Component
    / / define FinalWrappedComponent
    let FinalWrappedComponent = WrappedComponent

    if (pure) {
      // Use PureComponent for true
      OuterBaseComponent = PureComponent
    }
    Class Connect extends OuterBaseComponent

// 
function makeDerivedPropsSelector(a) {
      // Define a variable that holds the value from the last makeDerivedPropsSelector
      // It is not difficult to guess the meaning of variables semantic
      let lastProps
      let lastState
      let lastDerivedProps
      let lastStore
      let sourceSelector
      return function selectDerivedProps(state, props, store) {  // props for the parent component
        // If pure is true, the props and state references remain unchanged, returning the lastDerivedProps(which would not have been true the first time).
        // Props = props = props
        if (pure && lastProps === props && lastState === state) {
          return lastDerivedProps
        }

        if(store ! == lastStore) {// store assigns the value lastStore to update lastStore
          // Usually not except for the first call
          lastStore = store
          // selectorFactory is the function passed in by connect. The default value is from selsctorFactory.js export Default
          sourceSelector = selectorFactory(
            store.dispatch,
            selectorFactoryOptions  // Set all parameters)}// props to lastProps
        lastProps = props
        // State is assigned to lastState
        lastState = state

        // Call sourceSelector and enter redux state and props to get the latest props
        SelectorFactory returns a function. SelectorFactory returns a function
        Selsctorfactory.js export default function (dispatch, selectorFactoryOptions)=>(state, props) => newProps
        const nextProps = sourceSelector(state, props)

        // the old props are the same
        if (lastDerivedProps === nextProps) {
          // Return directly
          return lastDerivedProps
        }
        // The old and new props are different, nextProps is assigned to lastDerivedProps
        lastDerivedProps = nextProps
        / / return lastDerivedProps
        return lastDerivedProps
        // SelsctorFactory.js = props}}function makeChildElementSelector(a) {
      // Define the props, ref, element variables
      let lastChildProps, lastForwardRef, lastChildElement
      / / return function
      return function selectChildElement(childProps, forwardRef) {
        // Props, hre, elelment
        if(childProps ! == lastChildProps || forwardRef ! == lastForwardRef) {// If not, re-assign the value
          lastChildProps = childProps
          lastForwardRef = forwardRef
          lastChildElement = (
            // Return FinalWrappedComponent, props and ref<FinalWrappedComponent {... childProps} ref={forwardRef} /> ) }/ / the react components
        return lastChildElement
      }
    }

    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
        )
        // Add selectDerivedProps and selectChildElement methods
        SelectDerivedProps function is the return value of makeDerivedPropsSelector
        this.selectDerivedProps = makeDerivedPropsSelector()
        / / selectChildElement for function
        this.selectChildElement = makeChildElementSelector()
        // bind this
        this.renderWrappedComponent = this.renderWrappedComponent.bind(this)}{storeState: store.getState(),store}
      renderWrappedComponent(value) {
        invariant(
          value,
          `Could not find "store" in the context of ` +
            `"${displayName}". Either wrap the root component in a <Provider>, ` +
            `or pass a custom React context provider to <Provider> and the corresponding ` +
            `React context consumer to ${displayName} in connect options.`
        )

        // Obtain the redux state and store
        const { storeState, store } = value

        // Define wrapperProps as this.props
        let wrapperProps = this.props
        let forwardedRef
        ForwardRef = {ref} when forwardRef is true
        if (forwardRef) {
          // wrapperProps is the wrapperProps in props
          wrapperProps = this.props.wrapperProps
          // this must be the props for this function
          Let FinalWrappedComponent = FinalWrappedComponent; // Use this to pass to the child WrappedComponent
          forwardedRef = this.props.forwardedRef
        }

        / / export props
        let derivedProps = this.selectDerivedProps(
          storeState,
          wrapperProps,
          store
        )

        // Return the final component, passing in the final props and ref -> see selectChildElement provisioning
        return this.selectChildElement(derivedProps, forwardedRef)
      }

      render() {
        // The default is the public ReactReduxContext
        const ContextToUse = this.props.context || Context

        return (// <Privoder /> Consumer < contexttouse.consumer > {this.renderWrappedComponent}
          </ContextToUse.Consumer>
        )}}// Equivalent to a plug-in
    // the WrappedComponent is assigned to connect.wrappedcomponent
    Connect.WrappedComponent = WrappedComponent
    / / add the displayName
    Connect.displayName = displayName

    ForwardRef is true
    if (forwardRef) {
      // Use the react. ForwardRef to provide a grandchild component to the parent of the component generated by connect
      const forwarded = React.forwardRef(function forwardConnectRef( props, ref ) {
        return <Connect wrapperProps={props} forwardedRef={ref} />
      })

      Connect ()(
      ) {connect()(
      ) {connect()(
      ) {connect()(
      )
      forwarded.displayName = displayName
      forwarded.WrappedComponent = WrappedComponent
      return hoistStatics(forwarded, WrappedComponent)
    }

    // Merge non-react static properties or methods of the child component into the parent component
    // Return the Connect component that extended the props attribute
    return hoistStatics(Connect, WrappedComponent)}}Copy the code

ConnectAdvanced returns an react advanced component. This component is required by the pure,forwardRef configuration to determine whether to use the PureComponent and ref transfers. This component is required by the selectDerivedProps. Selsctorfactory.js: functions and functions. Selsctorfactory.js: functions and functions

selectorFactory

The selectorFactory function returns a selector function that calculates the new props based on the store state, the display component props, and the dispatch. The new props are injected into the container component

SelectorFactory. Js

import verifySubselectors from './verifySubselectors'

export function impureFinalPropsSelectorFactory( mapStateToProps, mapDispatchToProps, mergeProps, dispatch ) {
  return function impureFinalPropsSelector(state, ownProps) {
    // Execute mergePropsProxy and return the modified props
    return mergeProps(
      mapStateToProps(state, ownProps),  // mapStateToProps Execution result
      mapDispatchToProps(dispatch, ownProps), // Execution result of mapDispatchToProps
      ownProps // Props for me)}}export function pureFinalPropsSelectorFactoryMapStateToProps, mapDispatchToProps, mergeProps, dispatch, // areStatesEqual AreStatePropsEqual for shallowEqual {areStatesEqual, areOwnPropsEqual, areStatePropsEqual}) {
  // The hasRunAtLeastOnce tag is first executed
  Return (); // Return ()
  let hasRunAtLeastOnce = false
  let state
  let ownProps
  let stateProps
  let dispatchProps
  let mergedProps

  // The first execution
  function handleFirstCall(firstState, firstOwnProps) {
    // Assign the following results directly
    state = firstState
    ownProps = firstOwnProps
    stateProps = mapStateToProps(state, ownProps)
    dispatchProps = mapDispatchToProps(dispatch, ownProps)
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    // hasRunAtLeastOnce is marked true
    hasRunAtLeastOnce = true
    / / return mergedProps
    return mergedProps
  }

  function handleNewPropsAndNewState(a) {
    // Get the current new state
    stateProps = mapStateToProps(state, ownProps)

    if (mapDispatchToProps.dependsOnOwnProps)
      dispatchProps = mapDispatchToProps(dispatch, ownProps)

    // Return mergedProps function with the new object inside
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    return mergedProps
  }

  // props to change
  function handleNewProps(a) {
    // dependsOnOwnProps determines whether there is an ownprops parameter
    // If there are props that need to be reexecuted, get the new props for state_props and mapDispatchToProps because their props changed
    if (mapStateToProps.dependsOnOwnProps)
      stateProps = mapStateToProps(state, ownProps)

    if (mapDispatchToProps.dependsOnOwnProps)
      dispatchProps = mapDispatchToProps(dispatch, ownProps)

    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    return mergedProps
  }

  // Redux state changed
  function handleNewState(a) {
    const nextStateProps = mapStateToProps(state, ownProps)
    // Compare the nextStateProps and stateProps
    conststatePropsChanged = ! areStatePropsEqual(nextStateProps, stateProps)/ / update the stateProps
    stateProps = nextStateProps

    MergedProps must be recalculated. MergedProps returns a new object
    if (statePropsChanged)
      mergedProps = mergeProps(stateProps, dispatchProps, ownProps)

    return mergedProps
  }

  function handleSubsequentCalls(nextState, nextOwnProps) {
    // The ownProps are props
    constpropsChanged = ! areOwnPropsEqual(nextOwnProps, ownProps)// Compare references to redux state
    conststateChanged = ! areStatesEqual(nextState, state)// nextState is assigned to state
    state = nextState
    // nextOwnProps to onwProps
    ownProps = nextOwnProps

    // props && state change
    // Return function for different cases
    if (propsChanged && stateChanged) return handleNewPropsAndNewState()
    // props change
    if (propsChanged) return handleNewProps()
    // state change
    if (stateChanged) return handleNewState()
    PropsChanged, stateChanged = true props, state does not change, return mergedProps
    return mergedProps
  }

  return function pureFinalPropsSelector(nextState, nextOwnProps) {  // state props
    return hasRunAtLeastOnce // The default is false
      ? handleSubsequentCalls(nextState, nextOwnProps)
      : handleFirstCall(nextState, nextOwnProps)
  }
}

// Find export Default
/ * * * * *@export
 * @param {*} dispatch // store.dispatch
 * @param{*} {* initMapStateToProps // Struct initProxySelector(dispatch, {displayName}) => // initMergePropsProxy(dispatch, options) => mergePropsProxy * initMergeProps, *... Options Other configurations *} *@returns  selectorFactory function 
 */
/ / finalPropsSelectorFactory and our vision structure
export default function finalPropsSelectorFactory( dispatch, { initMapStateToProps, initMapDispatchToProps, initMergeProps, ... options } ) {
  // Call initProxySelector to get proxy function. Proxy contains mapToProps, dependsOnOwnProps
  const mapStateToProps = initMapStateToProps(dispatch, options)
  const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
  / / mergePropsProxy for function
  / / the return value is the connect (mapstate mapdispatch, function mergeProps () {}) (in) mergeProps return values
  const mergeProps = initMergeProps(dispatch, options)

  / / the production test of the environment mapStateToProps mapDispatchToProps, mergeProps
  if(process.env.NODE_ENV ! = ='production') {
    verifySubselectors(
      mapStateToProps,
      mapDispatchToProps,
      mergeProps,
      options.displayName
    )
  }

  // Pure is true to cache the return values of selectorFactory. Make the minimum possible changes to the current redux state and ownProps
  / / see pureFinalPropsSelectorFactory
  // Otherwise return the new object
  const selectorFactory = options.pure
    ? pureFinalPropsSelectorFactory
    : impureFinalPropsSelectorFactory

  / / selectorFactory execution
  // selectorFactory is a factory function that returns selector
  return selectorFactory(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps, // function mergePropsProxy
    dispatch,
    options
  )
}

Copy the code

MergedProps implementation

import verifyPlainObject from '.. /utils/verifyPlainObject'

export function defaultMergeProps(stateProps, dispatchProps, ownProps) {
  // Return a new object
  return{... ownProps, ... stateProps, ... dispatchProps } }export function wrapMergePropsFunc(mergeProps) {
  // initMergeProps
  return function initMergePropsProxy( dispatch, { displayName, pure, areMergedPropsEqual } ) {
    // For the first run, set to false
    let hasRunOnce = false
    let mergedProps

    return function mergePropsProxy(stateProps, dispatchProps, ownProps) {
      // Return the result of mergeProps
      const nextMergedProps = mergeProps(stateProps, dispatchProps, ownProps)

      if (hasRunOnce) {
        // Pure is fales or mergedProps is null
        if(! pure || ! areMergedPropsEqual(nextMergedProps, mergedProps)) mergedProps = nextMergedProps }else {
        // Not the first run, hasRunOnce is marked false
        hasRunOnce = true
        // nextMergedProps to mergedProps
        mergedProps = nextMergedProps

        if (process.env.NODE_ENV ! = ='production')
          verifyPlainObject(mergedProps, displayName, 'mergeProps')
      }

     // Return the modified props
      return mergedProps
    }
  }
}

export function whenMergePropsIsFunction(mergeProps) {
  // If mergeProps is true and function, call wrapMergePropsFunc and return function initMergePropsProxy(){}
  return typeof mergeProps === 'function'
    ? wrapMergePropsFunc(mergeProps)
    : undefined
}

export function whenMergePropsIsOmitted(mergeProps) {
  // mergeProps ! == true retun () => { ... ownProps, ... stateProps, ... dispatchProps }
  return! mergeProps ? () => defaultMergeProps : undefined }// Execute backwards and forwards
export default [whenMergePropsIsFunction, whenMergePropsIsOmitted]

/** * [mergeProps(stateProps, dispatchProps, ownProps): props] (Function): If this parameter is specified, the results of mapStateToProps() and mapDispatchToProps(), as well as the component's props, are passed to the callback function. * The object returned by this callback function is passed to the wrapped component as props */
Copy the code