Also let’s take a look at the common API used in React-redux;

Core API

  1. connect(mapStateToProps? , mapDispatchToProps? , mergeProps? , options?)
  • mapStateToProps? : (state, ownProps?) => Object
  • mapDispatchToProps? : Object | (dispatch, ownProps?) => Object
  • mergeProps? : (stateProps, dispatchProps, ownProps) => Object
  • options? : Object
  1. Provider

Props parameters:

  • store
  • Children: Element child node
  • Context: new content, usually not passed, with default ReactReduxContext inside
  1. Hooks method
  • useSelector(selector: Function, equalityFn? : Function)
  • useDispatch()
  • useStore()

Pre –

  1. React-redux implements the context of react.
  2. Subscription, Context,Provider, useReduxContext,useStore, useDispatch, useSelector, connect.

(Note: Due to too much logical code in CONNECT, I leave it to the end)

A: the Subscription

The React-Redux source code implements a publish subscriber itself

  • The Subscription code
export default class Subscription {
  constructor(store, parentSub) {
    this.store = store     // createStore creates a store object
    this.parentSub = parentSub   // This parameter is important, and subscription is often passed in the Provider
    this.unsubscribe = null
    this.listeners = nullListeners
    this.handleChangeWrapper = this.handleChangeWrapper.bind(this)}/* Add event subscription */
  addNestedSub(listener) {
    this.trySubscribe()
    return this.listeners.subscribe(listener)
  }
  /* Subscribe to event distribution */
  notifyNestedSubs() {
    this.listeners.notify()
  }
  handleChangeWrapper() {
    if (this.onStateChange) {
      this.onStateChange()
    }
  }
  isSubscribed() {
    return Boolean(this.unsubscribe)
  }
  
  /* Start subscribing */
  trySubscribe() {
    if (!this.unsubscribe) {
      /* There are two cases */
      /* If a subscriber already exists, add a new subscription */
      /* Situation 2: there is no old subscriber, here is to subscribe to store content, Provider is the case */
      this.unsubscribe = this.parentSub
        ? this.parentSub.addNestedSub(this.handleChangeWrapper)
        : this.store.subscribe(this.handleChangeWrapper)

      /* Realize a process */ based on the linked list
      this.listeners = createListenerCollection()
    }
  }
  /* Destroy the entire subscription */
  tryUnsubscribe() {
    if (this.unsubscribe) {
      this.unsubscribe()
      this.unsubscribe = null
      this.listeners.clear()
      this.listeners = nullListeners
    }
  }
}
Copy the code
  • The implementation of createListenerCollection is shown here to avoid too much space. It is placed in another path, which does not affect the overall reading.

2: the Context

import React from 'react''/* Create a default global context */ export const ReactReduxContext = /*#__PURE__*/ reace.createcontext (null) if (process.env.node_env ! = = 'production') { ReactReduxContext.displayName = 'ReactRedux'
}
export default ReactReduxContext
Copy the code

The new context API is created using React. CreateContext

3: the Provider

import React, { useMemo, useEffect } from 'react'
import { ReactReduxContext } from './Context'
import Subscription from '.. /utils/Subscription'

function Provider({ store, context, children }) {
  // Use useMemo to optimize performance
  const contextValue = useMemo(() = > {
    /* Create a publish subscriber */
    const subscription = new Subscription(store)
    subscription.onStateChange = subscription.notifyNestedSubs // This is a content ready to subscribe to the store
    return {
      store,
      subscription,
    }
  }, [store])  // Based on the store, the reference address normally does not change

  const previousState = useMemo(() = > store.getState(), [store])


  /* previousState is also based on store, so normally useEffect content is executed only once */
  useEffect(() = > {
    const { subscription } = contextValue
    subscription.trySubscribe()
    // Subscription this.store. Subscribe (this.handlechangewrapper)
    / / will subscription. NotifyNestedSubs subscription to the store

    /* For the first time, a unique execution is performed to distribute initialization */
    if(previousState ! == store.getState()) { subscription.notifyNestedSubs() }return () = > {
      subscription.tryUnsubscribe()  // Destroy the subscription
      subscription.onStateChange = null
    }
  }, [contextValue, previousState])  

  const Context = context || ReactReduxContext

  return <Context.Provider value={contextValue}>{children}</Context.Provider>
}
export default Provider
Copy the code

The core points in the code above are:

  1. With context. Provider, a global Context is provided
  2. Store, subscription is passed down by context
  3. Will the subscription. NotifyNestedSubs subscribe to the store

So let’s just speculate a little bit about what’s going to happen in useSelector Connect.

  • Take the subscription from context and add events to it via addNestedSub
  • Store. When dispatch will naturally perform subscription. NotifyNestedSubs, update into the event

This should give you a brief idea of the react-Redux process, otherwise go back to the previous code.

Four: useReduxContext

import { useContext } from 'react'
import { ReactReduxContext } from '.. /components/Context'
export function useReduxContext() {
  /* get contextValue */
  const contextValue = useContext(ReactReduxContext)
  return contextValue
}
Copy the code

The hooks API gets the value of the context via useContext

Five: useStore

import { useContext } from 'react'
import { ReactReduxContext } from '.. /components/Context'
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'

/* createStoreHook allows you to pass in additional defined context, normally using the default ReactReduxContext */
export function createStoreHook(context = ReactReduxContext) {
  const useReduxContext =
    context === ReactReduxContext
      ? useDefaultReduxContext
      : () = > useContext(context)
  return function useStore() {
    /* Get store */ from contextValue
    const { store } = useReduxContext()
    return store
  }
}
export const useStore = /*#__PURE__*/ createStoreHook()
Copy the code

Gets the store in contextValue of the Provider

Six: useDispatch

import { ReactReduxContext } from '.. /components/Context'
import { useStore as useDefaultStore, createStoreHook } from './useStore'

/* createDispatchHook also allows us to pass in an additional defined context, using the default ReactReduxContext */
export function createDispatchHook(context = ReactReduxContext) {
  const useStore =
    context === ReactReduxContext ? useDefaultStore : createStoreHook(context)

  return function useDispatch() {
    const store = useStore()
    /* Get the dispatch */ from the store
    return store.dispatch
  }
}
export const useDispatch = /*#__PURE__*/ createDispatchHook()
Copy the code

Get the dispatch from the store

Seven: useSelector

  • CreateSelectorHook code
import { useReducer, useRef, useMemo, useContext, useDebugValue } from 'react'
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
import { ReactReduxContext } from '.. /components/Context'
/* also allows us to pass in an additional defined context, using the default ReactReduxContext */
export function createSelectorHook(context = ReactReduxContext) {
  const useReduxContext =
    context === ReactReduxContext
      ? useDefaultReduxContext
      : () = > useContext(context)
  return function useSelector(selector, equalityFn = refEquality) {
    const { store, subscription: contextSub } = useReduxContext()
    const selectedState = useSelectorWithStoreAndSubscription(
      selector,
      equalityFn,
      store,
      contextSub
    )

    useDebugValue(selectedState)

    return selectedState
  }
}
export const useSelector = /*#__PURE__*/ createSelectorHook()
Copy the code

As you guessed, take the Subscription from the context and do the same

  • What does selector pass as an argument
/* Function */ defined by state 
* / / * for example
const todo = useSelector(state= > state.todos[props.id])
Copy the code
  • What is the equalityFn pass parameter
/* Compare function, what condition does not update */
const refEquality = (a, b) = >A = = = b;/ / the default value
Copy the code
  • useIsomorphicLayoutEffect
import { useEffect, useLayoutEffect } from 'react'
export const useIsomorphicLayoutEffect =
  typeof window! = ='undefined' &&
  typeof window.document ! = ='undefined' &&
  typeof window.document.createElement ! = ='undefined'
    ? useLayoutEffect
    : useEffect
Copy the code
  • UseSelectorWithStoreAndSubscription code
import { useReducer, useRef, useMemo, useContext, useDebugValue } from 'react'
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
import Subscription from '.. /utils/Subscription'
import { useIsomorphicLayoutEffect } from '.. /utils/useIsomorphicLayoutEffect'
import { ReactReduxContext } from '.. /components/Context'

function useSelectorWithStoreAndSubscription(selector, equalityFn, store, contextSub) {
  // View update, key code, emulated forceUpdate
  const [, forceRender] = useReducer((s) = > s + 1.0)

  // contentSub, which is the subscription in Provider
  const subscription = useMemo(() = > new Subscription(store, contextSub), [
    store,
    contextSub,
  ])

  /* Cache variable definition */
  const latestSubscriptionCallbackError = useRef()
  const latestSelector = useRef()
  const latestStoreState = useRef()
  const latestSelectedState = useRef()

  const storeState = store.getState()
  let selectedState

  try {
    if( selector ! == latestSelector.current || storeState ! == latestStoreState.current || latestSubscriptionCallbackError.current ) {// Only the first time, do the calculation, the rest use the cache variable content
      selectedState = selector(storeState)
    } else {
      selectedState = latestSelectedState.current
    }
  } catch (err) {
    if (latestSubscriptionCallbackError.current) {
      err.message += `\nThe error may be correlated with this previous error:\n${latestSubscriptionCallbackError.current.stack}\n\n`
    }
    throw err
  }

  // Cache objects
  useIsomorphicLayoutEffect(() = > {  // useEffect in browser environment
    latestSelector.current = selector
    latestStoreState.current = storeState
    latestSelectedState.current = selectedState
    latestSubscriptionCallbackError.current = undefined
  })

  useIsomorphicLayoutEffect(() = > {
    function checkForUpdates() {
      try {
        const newSelectedState = latestSelector.current(store.getState())

        // What condition does it meet
        if (equalityFn(newSelectedState, latestSelectedState.current)) {
          return
        }

        latestSelectedState.current = newSelectedState
      } catch (err) {
        latestSubscriptionCallbackError.current = err
      }

      forceRender() // View update
    }
    subscription.onStateChange = checkForUpdates
    subscription.trySubscribe() // Add updated content to the subscription in the Provider
    checkForUpdates()
    return () = > subscription.tryUnsubscribe()
  }, [store, subscription])

  return selectedState
}
Copy the code

The key code for the update is in there: Const [, forceRender] = useReducer((s) => s + 1, 0), then add the whole checkForUpdates, Add addNestedSub to the Provider subscription to get through the update process

Eight: the connect

export function createConnect({ connectHOC = connectAdvanced, mapStateToPropsFactories = defaultMapStateToPropsFactories, mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory, } = {}) {
  return function connect(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    {
      pure = true, areStatesEqual = strictEqual, areOwnPropsEqual = shallowEqual, areStatePropsEqual = shallowEqual, areMergedPropsEqual = shallowEqual, ... extraOptions } = {}) {
    // Parameter processing
    const initMapStateToProps = match(
      mapStateToProps,
      mapStateToPropsFactories,
      'mapStateToProps'
    )
    const initMapDispatchToProps = match(
      mapDispatchToProps,
      mapDispatchToPropsFactories,
      'mapDispatchToProps'
    )
    const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')

    return connectHOC(selectorFactory, {
      methodName: 'connect'.getDisplayName: (name) = > `Connect(${name}) `.shouldHandleStateChanges: Boolean(mapStateToProps), initMapStateToProps, initMapDispatchToProps, initMergeProps, pure, areStatesEqual, areOwnPropsEqual, areStatePropsEqual, areMergedPropsEqual, ... extraOptions, }) } }export default /*#__PURE__*/ createConnect()
Copy the code

Believe that for the first time to see the code, should be meng, explain, match, initMapStateToProps, initMapDispatchToProps, initMergeProps is handling of parameters

  • 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 The ${typeof arg} for ${name} argument when connecting component ${ options.wrappedComponentName }. `)}}Copy the code

Match with defaultMapStateToPropsFactories, defaultMapDispatchToPropsFactories defaultMergePropsFactories, parameter processing, In this process, bindActionCreators implemented the object-form processing, which returns mapDispatchToProps if there is a reasonable value

Let’s look at the implementation of selectorFactory in the main process.

  • This is the source code in 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)
  /* Pure is an optimization parameter and defaults to true */
  const selectorFactory = options.pure
    ? pureFinalPropsSelectorFactory
    : impureFinalPropsSelectorFactory

  return selectorFactory(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    dispatch,
    options
  )
}
Copy the code

One of the key attributes mentioned above is pure,

  • Let’s consider this question: Is it ok to return the same state references when reducer write? Answer: True for pure, but not recommended

  • impureFinalPropsSelectorFactory

/* Computes directly without any optimizations */
export function impureFinalPropsSelectorFactory(mapStateToProps, mapDispatchToProps, mergeProps, dispatch) {
  return function impureFinalPropsSelector(state, ownProps) {
    return mergeProps(
      mapStateToProps(state, ownProps),
      mapDispatchToProps(dispatch, ownProps),
      ownProps
    )
  }
}
Copy the code
  • pureFinalPropsSelectorFactory
/* Compare old and new values to optimize performance */
export function pureFinalPropsSelectorFactory(mapStateToProps, mapDispatchToProps, mergeProps, dispatch, { areStatesEqual, areOwnPropsEqual, areStatePropsEqual }) {
  let hasRunAtLeastOnce = false
  let state
  let ownProps
  let stateProps
  let dispatchProps
  let mergedProps

  /* The first time is also directly computed, but the initial value */ is recorded
  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() {
    stateProps = mapStateToProps(state, ownProps)

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

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

  function handleNewProps() {
    if (mapStateToProps.dependsOnOwnProps)
      stateProps = mapStateToProps(state, ownProps)

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

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

  function handleNewState() {
    const nextStateProps = mapStateToProps(state, ownProps)
    conststatePropsChanged = ! areStatePropsEqual(nextStateProps, stateProps)// shallowEqual method comparison
    stateProps = nextStateProps

    if (statePropsChanged)
      mergedProps = mergeProps(stateProps, dispatchProps, ownProps)

    return mergedProps
  }

  function handleSubsequentCalls(nextState, nextOwnProps) {
    constpropsChanged = ! areOwnPropsEqual(nextOwnProps, ownProps)// shallowEqual method comparison
    conststateChanged = ! areStatesEqual(nextState, state)// === strict comparison
    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)
  }
}
Copy the code

The comparison method involved in the code

  1. strictEqual
function strictEqual(a, b) {
  return a === b
}
Copy the code
  1. ShallowEqual (includes the first level comparison of objects)
function is(x, y) {
  if (x === y) {
    returnx ! = =0|| y ! = =0 || 1 / x === 1 / y
  } else {
    returnx ! == x && y ! == y } }export default function shallowEqual(objA, objB) {
  if (is(objA, objB)) return true
  if (
    typeofobjA ! = ='object' ||
    objA === null ||
    typeofobjB ! = ='object' ||
    objB === null
  ) {
    return false
  }
  const keysA = Object.keys(objA)
  const keysB = Object.keys(objB)
  if(keysA.length ! == keysB.length)return false
  for (let i = 0; i < keysA.length; i++) {
    if(!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || ! is(objA[keysA[i]], objB[keysA[i]]) ) {return false}}return true
}
Copy the code

As you can see, selectorFactory is also a parameter processing. It is not designed to update the core operation, so let’s go to the core connectHOC code.

  • connectAdvanced
export default function connectAdvanced(
  selectorFactory,
  {
    getDisplayName = (name) => `ConnectAdvanced(${name}) `,
    methodName = 'connectAdvanced',
    renderCountProp = undefined,
    shouldHandleStateChanges = true,
    storeKey = 'store',
    withRef = false,
    forwardRef = false, context = ReactReduxContext, ... connectOptions } = {}) {
  const Context = context

  return function wrapWithConnect(WrappedComponent) {
    const wrappedComponentName =
      WrappedComponent.displayName || WrappedComponent.name || 'Component'
    const displayName = getDisplayName(wrappedComponentName)
    constselectorFactoryOptions = { ... connectOptions, getDisplayName, methodName, renderCountProp, shouldHandleStateChanges, storeKey, displayName, wrappedComponentName, WrappedComponent, }const { pure } = connectOptions

    function createChildSelector(store) {
      return selectorFactory(store.dispatch, selectorFactoryOptions)
    }
    const usePureOnlyMemo = pure ? useMemo : (callback) = > callback()

    function ConnectFunction(props){
      // ...
    }
    const Connect = pure ? React.memo(ConnectFunction) : ConnectFunction

    Connect.WrappedComponent = WrappedComponent
    Connect.displayName = displayName

    if (forwardRef) {
      const forwarded = React.forwardRef(function forwardConnectRef(props, ref) {
        return <Connect {. props} reactReduxForwardedRef={ref} />
      })

      forwarded.displayName = displayName
      forwarded.WrappedComponent = WrappedComponent
      return hoistStatics(forwarded, WrappedComponent)
    }
    return hoistStatics(Connect, WrappedComponent)
  }
}
Copy the code

The forwardRef checks the presence of a high-order component and ensures that the ref instance hoistStatics is accessed internally as the hoist non-react-statics library to prevent static properties of high-order components from being lost.

  • Take a look at ConnectFunction above
   function ConnectFunction(props) {
      const [
        propsContext,
        reactReduxForwardedRef,
        wrapperProps,
      ] = useMemo(() = > {
        const{ reactReduxForwardedRef, ... wrapperProps } = propsreturn [props.context, reactReduxForwardedRef, wrapperProps]
      }, [props])

      /* Whether a context exists in the component props, otherwise ReactReduxContext is used by default
      const ContextToUse = useMemo(() = > {
        return propsContext &&
          propsContext.Consumer &&
          isContextConsumer(<propsContext.Consumer />)? propsContext : Context }, [propsContext, Context])// Get the contextValue in the Provider
      const contextValue = useContext(ContextToUse)
      const didStoreComeFromProps =
        Boolean(props.store) &&
        Boolean(props.store.getState) &&
        Boolean(props.store.dispatch)
      const didStoreComeFromContext =
        Boolean(contextValue) && Boolean(contextValue.store)
      const store = didStoreComeFromProps ? props.store : contextValue.store

      const childPropsSelector = useMemo(() = > {
        return createChildSelector(store)
      }, [store])

      // Subscription and event publishing in Provider
      const [subscription, notifyNestedSubs] = useMemo(() = > {
        if(! shouldHandleStateChanges)return NO_SUBSCRIPTION_ARRAY
        const subscription = new Subscription(
          store,
          didStoreComeFromProps ? null : contextValue.subscription
        )
        const notifyNestedSubs = subscription.notifyNestedSubs.bind(
          subscription
        )

        return [subscription, notifyNestedSubs]
      }, [store, didStoreComeFromProps, contextValue])

      const overriddenContextValue = useMemo(() = > {
        if (didStoreComeFromProps) {
          return contextValue
        }
        return {
          ...contextValue,
          subscription,
        }
      }, [didStoreComeFromProps, contextValue, subscription])

      /* Force update */
      const [
        [previousStateUpdateResult],
        forceComponentUpdateDispatch,
      ] = useReducer(storeStateUpdatesReducer, EMPTY_ARRAY, initStateUpdates)
      if (previousStateUpdateResult && previousStateUpdateResult.error) {
        throw previousStateUpdateResult.error
      }
      const lastChildProps = useRef()
      const lastWrapperProps = useRef(wrapperProps)
      const childPropsFromStoreUpdate = useRef()
      const renderIsScheduled = useRef(false)

      const actualChildProps = usePureOnlyMemo(() = > {
        if (
          childPropsFromStoreUpdate.current &&
          wrapperProps === lastWrapperProps.current
        ) {
          return childPropsFromStoreUpdate.current
        }
        return childPropsSelector(store.getState(), wrapperProps)
      }, [store, previousStateUpdateResult, wrapperProps])

      / * useIsomorphicLayoutEffectWithArgs same as useEffect * /
      useIsomorphicLayoutEffectWithArgs(captureWrapperProps, [
        lastWrapperProps,
        lastChildProps,
        renderIsScheduled,
        wrapperProps,
        actualChildProps,
        childPropsFromStoreUpdate,
        notifyNestedSubs,
      ])

      /* This process includes subscribing to update events */
      useIsomorphicLayoutEffectWithArgs(
        subscribeUpdates,
        [
          shouldHandleStateChanges,
          store,
          subscription,
          childPropsSelector,
          lastWrapperProps,
          lastChildProps,
          renderIsScheduled,
          childPropsFromStoreUpdate,
          notifyNestedSubs,
          forceComponentUpdateDispatch,
        ],
        [store, subscription, childPropsSelector]
      )
      
      const renderedWrappedComponent = useMemo(
        () = > (
          <WrappedComponent
            {. actualChildProps}
            ref={reactReduxForwardedRef}
          />
        ),
        [reactReduxForwardedRef, WrappedComponent, actualChildProps]
      )
      const renderedChild = useMemo(() = > {
        if (shouldHandleStateChanges) {
          return (
            <ContextToUse.Provider value={overriddenContextValue}>
              {renderedWrappedComponent}
            </ContextToUse.Provider>)}return renderedWrappedComponent
      }, [ContextToUse, renderedWrappedComponent, overriddenContextValue])

      return renderedChild
    }
Copy the code
  • captureWrapperProps
function captureWrapperProps(lastWrapperProps, lastChildProps, renderIsScheduled, wrapperProps, actualChildProps, childPropsFromStoreUpdate, notifyNestedSubs) {
  lastWrapperProps.current = wrapperProps
  lastChildProps.current = actualChildProps
  renderIsScheduled.current = false
  // The first update is triggered
  if (childPropsFromStoreUpdate.current) {
    childPropsFromStoreUpdate.current = null
    notifyNestedSubs()
  }
}
Copy the code
  • subscribeUpdates
function subscribeUpdates(shouldHandleStateChanges, store, subscription, childPropsSelector, lastWrapperProps, lastChildProps, renderIsScheduled, childPropsFromStoreUpdate, notifyNestedSubs, forceComponentUpdateDispatch) {
  if(! shouldHandleStateChanges)return
  let didUnsubscribe = false
  let lastThrownError = null
  const checkForUpdates = () = > {
    if (didUnsubscribe) {
      return
    }

    const latestStoreState = store.getState()

    let newChildProps, error
    try {
      newChildProps = childPropsSelector(
        latestStoreState,
        lastWrapperProps.current
      )
    } catch (e) {
      error = e
      lastThrownError = e
    }

    if(! error) { lastThrownError =null
    }
    if (newChildProps === lastChildProps.current) {
      if(! renderIsScheduled.current) { notifyNestedSubs() } }else {
      lastChildProps.current = newChildProps
      childPropsFromStoreUpdate.current = newChildProps
      renderIsScheduled.current = true
      forceComponentUpdateDispatch({
        type: 'STORE_UPDATED'.payload: {
          error,
        },
      })
    }
  }
  
  // useSelector same routine
  subscription.onStateChange = checkForUpdates
  subscription.trySubscribe()
  checkForUpdates()

  const unsubscribeWrapper = () = > {
    didUnsubscribe = true
    subscription.tryUnsubscribe()
    subscription.onStateChange = null

    if (lastThrownError) {
      throw lastThrownError
    }
  }

  return unsubscribeWrapper
}
Copy the code

The above process, using the same routines, and useSelector by forceComponentUpdateDispatch and subscribe to the update process, the entire core update process has been finished.