react-redux源码理解与实现

495 阅读6分钟

基于源码版本号:v7.2.2
老版本的最简单实现
同样我们来看一下react-redux中常用的api;

核心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参数:
  • store
  • children: element子节点
  • context: 新的content,一般不传,内部存在默认ReactReduxContext
  1. hooks 方法
  • useSelector(selector: Function, equalityFn?: Function)
  • useDispatch()
  • useStore()

前置

  1. react-redux实现的基础: react中的context,不懂的可以先去官网看下.
  2. 整个过程,我会按照 Subscription,Context,Provider,useReduxContext,useStore,useDispatch,useSelector,connect顺序,尽可能的讲清除整个订阅更新流程.
        (备注:由于connect中逻辑代码太多,我放到最后讲)

一: Subscription

react-redux源码内部自己实现了一个发布订阅器

  • Subscription代码
export default class Subscription {
  constructor(store, parentSub) {
    this.store = store     // createStore创建的store对象
    this.parentSub = parentSub   // 这个参数很重要,很多时候传入Provider中的subscription
    this.unsubscribe = null
    this.listeners = nullListeners
    this.handleChangeWrapper = this.handleChangeWrapper.bind(this)
  }
  /* 添加事件订阅 */
  addNestedSub(listener) {
    this.trySubscribe()
    return this.listeners.subscribe(listener)
  }
  /* 订阅事件派发 */
  notifyNestedSubs() {
    this.listeners.notify()
  }
  handleChangeWrapper() {
    if (this.onStateChange) {
      this.onStateChange()
    }
  }
  isSubscribed() {
    return Boolean(this.unsubscribe)
  }
  
  /* 开始订阅 */
  trySubscribe() {
    if (!this.unsubscribe) {
      /* 存在2中情况 */
      /* 情况一:已经存在订阅器,这添加一个新的订阅 */
      /* 情况二:不存在老的订阅器,这里往store中订阅个内容,Provider中就是这种情况 */
      this.unsubscribe = this.parentSub
        ? this.parentSub.addNestedSub(this.handleChangeWrapper)
        : this.store.subscribe(this.handleChangeWrapper)

      /* 基于链表实现了一个listeners */
      this.listeners = createListenerCollection()
    }
  }
  /* 摧毁整个订阅 */
  tryUnsubscribe() {
    if (this.unsubscribe) {
      this.unsubscribe()
      this.unsubscribe = null
      this.listeners.clear()
      this.listeners = nullListeners
    }
  }
}
  • createListenerCollection的实现看这里,避免篇幅太长,放在了其他路径下,不影响整体阅读。

二:Context

import React from 'react''
/* 创建默认的全局context */
export const ReactReduxContext = /*#__PURE__*/ React.createContext(null)
if (process.env.NODE_ENV !== 'production') {
  ReactReduxContext.displayName = 'ReactRedux'
}
export default ReactReduxContext

新的context API通过React.createContext创建

三:Provider

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

function Provider({ store, context, children }) {
  // 借助useMemo进行性能优化,使用上:依赖参数只进行浅比较
  const contextValue = useMemo(() => {
    /* 创建发布订阅器 */
    const subscription = new Subscription(store)
    subscription.onStateChange = subscription.notifyNestedSubs // 这是一个准备订阅到store中的内容
    return {
      store,
      subscription,
    }
  }, [store])  // 基于store,正常情况下引用地址都不会变

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


  /* previousState也是根据store,所以正常情况下,useEffect内容只进行一次执行 */
  useEffect(() => {
    const { subscription } = contextValue
    subscription.trySubscribe()
    // 对应Subscription 中this.store.subscribe(this.handleChangeWrapper)
    // 将subscription.notifyNestedSubs订阅到store中

    /* 首次会进行唯一一次执行,进行派发初始化 */
    if (previousState !== store.getState()) {
      subscription.notifyNestedSubs()
    }
    return () => {
      subscription.tryUnsubscribe()  // 摧毁订阅
      subscription.onStateChange = null
    }
  }, [contextValue, previousState])  

  const Context = context || ReactReduxContext

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

上面代码中比较核心的点是:

  1. 通过Context.Provider,提供了一个全局context
  2. store,subscription通过context向下传递
  3. 将subscription.notifyNestedSubs订阅到了store中

这时候我们简单推测下,useSelector,connect中会做些什么?

  • 拿到context中的 subscription, 通过addNestedSub往里面加事件(数据更新,组件render)
  • store.dispatch时自然会执行subscription.notifyNestedSubs,更新放入的事件

看到这里,对react-redux整个流程应该有个简单了解,不然请回头再看下前面的代码。

四:useReduxContext

import { useContext } from 'react'
import { ReactReduxContext } from '../components/Context'
export function useReduxContext() {
  /* 拿到contextValue */
  const contextValue = useContext(ReactReduxContext)
  return contextValue
}

hooks API通过useContext获取到context的值

五:useStore

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

/* createStoreHook允许传入额外定义的context, 正常情况下用默认的ReactReduxContext 就行  */
export function createStoreHook(context = ReactReduxContext) {
  const useReduxContext =
    context === ReactReduxContext
      ? useDefaultReduxContext
      : () => useContext(context)
  return function useStore() {
    /* 从contextValue中拿到store */
    const { store } = useReduxContext()
    return store
  }
}
export const useStore = /*#__PURE__*/ createStoreHook()

获取Provider中 contextValue中的store

六:useDispatch

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

/* createDispatchHook同一允许我们传入额外定义的context, 用默认的ReactReduxContext就行 */
export function createDispatchHook(context = ReactReduxContext) {
  const useStore =
    context === ReactReduxContext ? useDefaultStore : createStoreHook(context)

  return function useDispatch() {
    const store = useStore()
    /* 拿到store中的dispatch */
    return store.dispatch
  }
}
export const useDispatch = /*#__PURE__*/ createDispatchHook()

从store中拿到dispatch

七:useSelector

  • createSelectorHook代码
import { useReducer, useRef, useMemo, useContext, useDebugValue } from 'react'
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
import { ReactReduxContext } from '../components/Context'
/* 一样允许我们传入额外定义的context, 用默认的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()

上面的操作和猜测的一样,从context中拿到subscription,并进行操作

  • selector传参是什么
/* 定义的根据state计算,的function */ 
/* 例如 */
const todo = useSelector(state => state.todos[props.id])
  • equalityFn传参是什么
/* 比较函数,满足什么条件不进行更新 */
const refEquality = (a, b) => a === b;// 默认值
  • useIsomorphicLayoutEffect
import { useEffect, useLayoutEffect } from 'react'
export const useIsomorphicLayoutEffect =
  typeof window !== 'undefined' &&
  typeof window.document !== 'undefined' &&
  typeof window.document.createElement !== 'undefined'
    ? useLayoutEffect
    : useEffect
  • useSelectorWithStoreAndSubscription代码
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
) {
  // 视图更新,关键代码,模拟forceUpdate
  const [, forceRender] = useReducer((s) => s + 1, 0)

  // contentSub也就是Provider中的那个subscription
  const subscription = useMemo(() => new Subscription(store, contextSub), [
    store,
    contextSub,
  ])

  /* 缓存变量定义 */
  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
    ) {
      // 只在第一次时,进行计算,剩下的都使用缓存变量内容
      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
  }

  // 缓存对象
  useIsomorphicLayoutEffect(() => {  // 浏览器环境下就是useEffect
    latestSelector.current = selector
    latestStoreState.current = storeState
    latestSelectedState.current = selectedState
    latestSubscriptionCallbackError.current = undefined
  })

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

        // 满足什么条件,不进行更新
        if (equalityFn(newSelectedState, latestSelectedState.current)) {
          return
        }

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

      forceRender() // 视图更新
    }
    subscription.onStateChange = checkForUpdates
    subscription.trySubscribe() // 往Provider中subscription中添加更新的内容
    checkForUpdates()
    return () => subscription.tryUnsubscribe()
  }, [store, subscription])

  return selectedState
}

更新的关键代码就在里面: const [, forceRender] = useReducer((s) => s + 1, 0),视图更新的关键,
然后将整个checkForUpdates,通过addNestedSub加入到Provider的subscription,打通整个更新流程

八: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
    } = {}
  ) {
    // 参数处理
    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()

相信第一次看到代码的时候,应该是懵的,解释下,match,initMapStateToProps,initMapDispatchToProps, initMergeProps是对参数的处理

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

match配合 defaultMapStateToPropsFactories, defaultMapDispatchToPropsFactories, defaultMergePropsFactories, 进行参数处理,存在合理值就返回
mapDispatchToProps的对象形式处理就是在这一过程中借助bindActionCreators实现

我们再看看主流程中selectorFactory的实现。

  • 这是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是一个优化参数,默认为true */
  const selectorFactory = options.pure
    ? pureFinalPropsSelectorFactory
    : impureFinalPropsSelectorFactory

  return selectorFactory(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    dispatch,
    options
  )
}

上面涉及到了一个关键属性pure,

  • 来个思考题: reducer书写时返回相同的state引用可以吗?
    答案:pure改成false的话可以,但不建议这么做

  • impureFinalPropsSelectorFactory

/*  直接计算,不进行任何优化  */
export function impureFinalPropsSelectorFactory(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  dispatch
) {
  return function impureFinalPropsSelector(state, ownProps) {
    return mergeProps(
      mapStateToProps(state, ownProps),
      mapDispatchToProps(dispatch, ownProps),
      ownProps
    )
  }
}
  • pureFinalPropsSelectorFactory
/*  新老值进行比较,进行性能优化  */
export function pureFinalPropsSelectorFactory(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  dispatch,
  { areStatesEqual, areOwnPropsEqual, areStatePropsEqual }
) {
  let hasRunAtLeastOnce = false
  let state
  let ownProps
  let stateProps
  let dispatchProps
  let mergedProps

  /* 第一次也是直接计算,但记录了初始值  */
  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)
    const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps)  //  shallowEqual方式比较
    stateProps = nextStateProps

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

    return mergedProps
  }

  function handleSubsequentCalls(nextState, nextOwnProps) {
    const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)  // shallowEqual方式比较
    const stateChanged = !areStatesEqual(nextState, state) //  === 严格比较
    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)
  }
}

代码中涉及到的比较方式

  1. strictEqual
function strictEqual(a, b) {
  return a === b
}
  1. shallowEqual(包涵了对象的第一层比较)
function is(x, y) {
  if (x === y) {
    return x !== 0 || y !== 0 || 1 / x === 1 / y
  } else {
    return x !== x && y !== y
  }
}

export default function shallowEqual(objA, objB) {
  if (is(objA, objB)) return true
  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== '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
}

上面可以看到,其实selectorFactory也是对参数的处理,没有设计到更新的核心操作,下面进入核心的connectHOC代码内,

  • 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)
    const selectorFactoryOptions = {
      ...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)
  }
}

可以看到返回的就是一个高阶组件,并forwardRef进行了判断,存在时,保证内部可访问到ref实例
hoistStatics 为工具库 hoist-non-react-statics,防止高阶组件静态属性丢失.

  • 上面中的ConnectFunction 来看一下
   function ConnectFunction(props) {
      const [
        propsContext,
        reactReduxForwardedRef,
        wrapperProps,
      ] = useMemo(() => {
        const { reactReduxForwardedRef, ...wrapperProps } = props
        return [props.context, reactReduxForwardedRef, wrapperProps]
      }, [props])

      /*  组件props中是否存在context,否则默认使用ReactReduxContext */
      const ContextToUse = useMemo(() => {
        return propsContext &&
          propsContext.Consumer &&
          isContextConsumer(<propsContext.Consumer />)
          ? propsContext
          : Context
      }, [propsContext, Context])

      // 获取到Provider中的contextValue
      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])

      // Provider中的subscription及其事件发布
      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])

      /* 强制更新 */
      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同样理解成useEffect即可*/
      useIsomorphicLayoutEffectWithArgs(captureWrapperProps, [
        lastWrapperProps,
        lastChildProps,
        renderIsScheduled,
        wrapperProps,
        actualChildProps,
        childPropsFromStoreUpdate,
        notifyNestedSubs,
      ])

      /*该过程包括了更新事件的订阅*/
      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
    }
  • captureWrapperProps
function captureWrapperProps(
  lastWrapperProps,
  lastChildProps,
  renderIsScheduled,
  wrapperProps,
  actualChildProps,
  childPropsFromStoreUpdate,
  notifyNestedSubs
) {
  lastWrapperProps.current = wrapperProps
  lastChildProps.current = actualChildProps
  renderIsScheduled.current = false
  // 首次更新触发
  if (childPropsFromStoreUpdate.current) {
    childPropsFromStoreUpdate.current = null
    notifyNestedSubs()
  }
}
  • 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相同的套路
  subscription.onStateChange = checkForUpdates
  subscription.trySubscribe()
  checkForUpdates()

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

    if (lastThrownError) {
      throw lastThrownError
    }
  }

  return unsubscribeWrapper
}

上面过程中,使用了和useSelector相同的套路,通过forceComponentUpdateDispatch并订阅来实现更新过程,整个核心更新流程已经写完了。