ZHANGYU.dev

October 14, 2023

React-Redux 源码阅读笔记

JavaScriptReact24.7 min to read

自从我学习过高阶组件,大家流传的就是connect()就是一个高阶组件,所以在我长久的认知中,认为connect()仅仅是一个从context中筛选出state,传入给子组件的简单高阶组件

我个人用redux是比较少的,自从hooks出了以后,就经常用useContext+useReducer来实现简单的redux的功能,所以也认为在react-redux里,connect()会像useReducer 一样,数据重新渲染React控制

结论当然不是这样,并且connect()组件也不简单,可以说在我打开源码的第一时间傻眼了,下面就来好好的分析下react-redux

原理简述

在分析源码之前,需要先将react-redux的原理简述一下,否则光干看代码还是会比较懵

先简单的画一个流程

image

react-redux的核心机制是通知订阅模式,源码中有一个Subscription类,它的作用主要是订阅父级的更新和通知子级的更新,也就是它既可以订阅别人,别人也可以订阅它,同时可以通知订阅它的Subscription

最外层的Provider组件的Context里包含了的store(也就是我们传入的)和生成的Subscription实例,它的Subscription实例订阅的则是reduxsubscrib()

当我们使用了connect()时,它会生成一个新组件<Component1/><Component1/>里会生成一个Subscription实例,它会订阅父级(这时是Provider)的Subscription实例,同时将自己的Subscription覆盖进Context,再包装我们传入的组件,如下模式

// overriddenContextValue包含了新组件的Subscription实例和store<Component1.Provider value={overriddenContextValue}>  {WrappedComponent}</Component1.Provider>

如果在<Component1/>里的子组件又有connect(),那么生成的<Component2/>组件的Subscription实例会订阅父级<Component1/>Subscription实例,同时再将自己的Subscription覆盖进Context

 在组件挂载完成后,如果store有更新,Provider会通知下一级组件的Subscription,下一级组件又回通知自己的下一级组件

<Provider store={store}>  <Component1> // 它订阅的Provider  	<Component2/> // 它订阅的Component2  <Component1/></Provider>// 当store有更新,Provider通知Component1,Component1通知Component2

在订阅的时候,会将更新自己组件的方法通过回调onStateChange()传入父级的Subscription

一旦父级接收到通知,就会循环调用订阅自己的组件的onStateChange来更新它们

更新的原理就是使用我们传入的mapStateToPropsmapDispatchToProps,结合内置的selectorFactor()来对比stateprops,一旦有改变就强制更新自己,所以我们传入的WrappedComponent也被强制更新了

原理简单来讲就是这样,下面来看源码

源码简析

在顺着流程分析之前,先看看贯通整个react-redux更新流程的Subscription

Subscription

// Subscriotion.jsconst nullListeners = { notify() {} };// 监听集合是一个双向链表function createListenerCollection() {  // 也就是React里的unstable_batchedUpdates  // 来自司徒正美微博:unstable_batchedUpdates会把子组件的forceUpdate干掉,防止组件在一个批量更新中重新渲染两次  const batch = getBatch();  let first = null;  let last = null;  return {    clear() {      first = null;      last = null;    },    // 通知订阅者更新    notify() {      batch(() => {        let listener = first;        while (listener) {          // 这个callback的本质就是让组件本身forceUpdate          listener.callback();          listener = listener.next;        }      });    },    // 订阅    subscribe(callback) {      let isSubscribed = true;      // 把last赋值为新的      let listener = (last = {        callback,        next: null,        prev: last      });      // 如果存在前一个,就把前一个的next指向当前(最后一个)      if (listener.prev) {        listener.prev.next = listener;      } else {        // 否则它就是第一个        first = listener;      }      // 返回退订函数      return function unsubscribe() {      	// ...退订逻辑      };    }  };}export default class Subscription {  constructor(store, parentSub) {    // redux store    this.store = store;    // 父级的Subscription实例    this.parentSub = parentSub;    // 退订函数    this.unsubscribe = null;    // 监听者    this.listeners = nullListeners;    this.handleChangeWrapper = this.handleChangeWrapper.bind(this);  }  // 添加嵌套的订阅者  addNestedSub(listener) {    // 首先先将当前的Subscription实例绑定到父级    // 绑定的同时会初始化listeners    this.trySubscribe();    return this.listeners.subscribe(listener);  }  // 通知子级  notifyNestedSubs() {    this.listeners.notify();  }  // 当父级Subscription的listeners通知时调用  handleChangeWrapper() {    // 这个是new出实例的时候加上的,感觉有点秀    if (this.onStateChange) {      this.onStateChange();    }  }  trySubscribe() {    // 不会重复绑定    if (!this.unsubscribe) {      this.unsubscribe = this.parentSub        ? this.parentSub.addNestedSub(this.handleChangeWrapper)        : // subscribe是redux里的方法,在redux state改变的时候会调用          this.store.subscribe(this.handleChangeWrapper);      // 创建新的listeners,每个connect的组件都会有listeners      this.listeners = createListenerCollection();    }  }  // 退订  tryUnsubscribe() {    if (this.unsubscribe) {      this.unsubscribe();      this.unsubscribe = null;      this.listeners.clear();      this.listeners = nullListeners;    }  }}

省略了一些代码,Subscription类主要就是创建一个既有监听功能又有订阅功能的对象

接下来就顺着流程来逐步分析,首先先看Provider里实现了什么

Provider

// components/Provider.jsfunction Provider({ store, context, children }) {  // useMemo仅在store变化时再重新返回  const contextValue = useMemo(() => {    const subscription = new Subscription(store);    // 通知订阅这个subscription的子级刷新    subscription.onStateChange = subscription.notifyNestedSubs;    return {      store,      // 将此subscription传入context方便子级订阅      subscription    };  }, [store]);  // 缓存上次的state  const previousState = useMemo(() => store.getState(), [store]);  useEffect(() => {    const { subscription } = contextValue;    // 在这里是订阅的reudx store的subscribe事件    subscription.trySubscribe();    if (previousState !== store.getState()) {      subscription.notifyNestedSubs();    }    return () => {      subscription.tryUnsubscribe();      subscription.onStateChange = null;    };  }, [contextValue, previousState, store]);  // 传入的context或者react-redux自带的  const Context = context || ReactReduxContext;  return <Context.Provider value={contextValue}>{children}</Context.Provider>;}

Provider是一个比较简单的组件,主要做了2件事

接下来看看核心connect组件

connect

// connect.js// 遍历执行函数,将arg作为参数传入,如果有结果则returnfunction match(arg, factories, name) {  for (let i = factories.length - 1; i >= 0; i--) {    const result = factories[i](arg);    if (result) return result;  }   // ... error}// ...export function createConnect({  // 默认值  connectHOC = connectAdvanced,  mapStateToPropsFactories = defaultMapStateToPropsFactories,  mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,  mergePropsFactories = defaultMergePropsFactories,  selectorFactory = defaultSelectorFactory} = {}) {  return function connect(    mapStateToProps,    mapDispatchToProps,    mergeProps,    {      pure = true,    	// ...省略一些参数    } = {}  ) {    const initMapStateToProps = match(      mapStateToProps,      mapStateToPropsFactories,      "mapStateToProps"    );    const initMapDispatchToProps = match(      mapDispatchToProps,      mapDispatchToPropsFactories,      "mapDispatchToProps"    );    const initMergeProps = match(mergeProps, mergePropsFactories, "mergeProps");    return connectHOC(selectorFactory, {      initMapStateToProps,      initMapDispatchToProps,      initMergeProps,      // ...省略了一些options    });  };}export default /*#__PURE__*/ createConnect();

connect()其实是由createConnect()默认创建出来的,虽然我们也可以调用createConnect()创建自定义的connect(),但是基本用不上

可以看到我们传入的mapStateToPropsmapDispatchToPropsmergeProps实际上是通过了一个match()函数的包装校验

这里就以mapStateToPropsFactories也就是defaultMapStateToPropsFactories为例

mapStateToPropsFactories

// mapStateToProps.jsimport { wrapMapToPropsConstant, wrapMapToPropsFunc } from "./wrapMapToProps";export function whenMapStateToPropsIsFunction(mapStateToProps) {  return typeof mapStateToProps === "function"    ? wrapMapToPropsFunc(mapStateToProps, "mapStateToProps")    : undefined;}export function whenMapStateToPropsIsMissing(mapStateToProps) {  return !mapStateToProps ? wrapMapToPropsConstant(() => ({})) : undefined;}export default [whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing];

match校验的时候,首先会判断我们是否传入的mapStateToProps,没有传入则调用wrapMapToPropsConstant创建一个默认方法

如果传入则会调用wrapMapToPropsFunc对我们的方法做一层包装,主要判断我们的方法是否需要依赖props

wrapMapToProps
// wrapMapToProps.js// ...//export function wrapMapToPropsFunc(mapToProps, methodName) {  return function initProxySelector(dispatch, { displayName }) {    const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {      return proxy.dependsOnOwnProps        ? proxy.mapToProps(stateOrDispatch, ownProps)        : proxy.mapToProps(stateOrDispatch);    };    // 根据dependsOnOwnProps的值来判断是否需要在props改变时重新调用    // 默认为true,因为要使用detectFactoryAndVerify    proxy.dependsOnOwnProps = true;    proxy.mapToProps = function detectFactoryAndVerify(      stateOrDispatch,      ownProps    ) {      // detectFactoryAndVerify方法只会调用一次      // 第一次调用后就会被我们传入的mapToProps覆盖掉      proxy.mapToProps = mapToProps;      // 这里会判断函数是否依赖于props      // getDependsOnOwnProps()的主要逻辑就是判断函数的参数个数,如果依赖props则参数等于2,返回true      proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps);      // 这时的值是由我们传入的mapToProps返回的      let props = proxy(stateOrDispatch, ownProps);      // 如果props是一个函数的情况在官方文档有讲,不过感觉这应该是高阶用法了,小张没有用过      // https://react-redux.js.org/api/connect#factory-functions      if (typeof props === "function") {        proxy.mapToProps = props;        proxy.dependsOnOwnProps = getDependsOnOwnProps(props);        props = proxy(stateOrDispatch, ownProps);      }      return props;    };    return proxy;  };}

这里的判断实际都是为了给后面的selectorFactory铺路,它的作用是根据stateprops的变化,判断是否需要调用我们的mapStateToPropsmapDispatchToPropsmergeProps返回新的数据

下面看看selectorFactory的实现

selectorFactory

// selectorFactory.js// 如果pure为false,则每次都会调用我们都mapStateToProps方法获得新的数据export function impureFinalPropsSelectorFactory(  mapStateToProps,  mapDispatchToProps,  mergeProps,  dispatch) {  return function impureFinalPropsSelector(state, ownProps) {    return mergeProps(      mapStateToProps(state, ownProps),      mapDispatchToProps(dispatch, ownProps),      ownProps    );  };}// pure为true时会判断值是否相同,不相同才调用,pure默认为trueexport function pureFinalPropsSelectorFactory(  mapStateToProps,  mapDispatchToProps,  mergeProps,  dispatch,  { areStatesEqual, areOwnPropsEqual, areStatePropsEqual }) {  // 至少运行过一次  let hasRunAtLeastOnce = false;  // 保存下来做对比  let state;  let ownProps;  let stateProps;  let dispatchProps;  let mergedProps;  // 第一次调用Selector初始化把值都存下来,方便后面的比较  function handleFirstCall(firstState, firstOwnProps) {    state = firstState;    ownProps = firstOwnProps;    stateProps = mapStateToProps(state, ownProps);    dispatchProps = mapDispatchToProps(dispatch, ownProps);    mergedProps = mergeProps(stateProps, dispatchProps, ownProps);    hasRunAtLeastOnce = true;    // 返回的都是mergedProps    return mergedProps;  }  function handleNewPropsAndNewState() {    // 重新计算redux store产生的props    stateProps = mapStateToProps(state, ownProps);    // 如果mapDispatchToProps需要根据props来改变,就需要重新计算    if (mapDispatchToProps.dependsOnOwnProps)      dispatchProps = mapDispatchToProps(dispatch, ownProps);    // 将redux props和dispatch props和传入组件的props合并    mergedProps = mergeProps(stateProps, dispatchProps, ownProps);    return mergedProps;  }  function handleNewProps() {    // 如果mapStateToProps需要获取组件的props,就需要重新计算    if (mapStateToProps.dependsOnOwnProps)      stateProps = mapStateToProps(state, ownProps);    // 如果mapDispatchToProps需要获取组件的props,就需要重新计算    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);    stateProps = nextStateProps;    // 只有改变了才重新merge    if (statePropsChanged)      mergedProps = mergeProps(stateProps, dispatchProps, ownProps);    return mergedProps;  }  function handleSubsequentCalls(nextState, nextOwnProps) {    const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps);    const stateChanged = !areStatesEqual(nextState, state);    state = nextState;    ownProps = nextOwnProps;    // 如果props和state都改变了    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);  };}// 最终返回的函数export default function finalPropsSelectorFactory(  dispatch,  { initMapStateToProps, initMapDispatchToProps, initMergeProps, ...options }) {  // 传入的函数全部经过了wrapMapToProps.js里的wrapMapToPropsFunc重新包装(proxy)  const mapStateToProps = initMapStateToProps(dispatch, options);  const mapDispatchToProps = initMapDispatchToProps(dispatch, options);  const mergeProps = initMergeProps(dispatch, options);  if (process.env.NODE_ENV !== "production") {    verifySubselectors(      mapStateToProps,      mapDispatchToProps,      mergeProps,      options.displayName    );  }  // pure浅对比调用pureFinalPropsSelectorFactory,里面会对比是否需要更新  const selectorFactory = options.pure    ? pureFinalPropsSelectorFactory    : impureFinalPropsSelectorFactory;  // 如果是pure返回的也就是pureFinalPropsSelectorFactory里的pureFinalPropsSelector函数  return selectorFactory(    mapStateToProps,    mapDispatchToProps,    mergeProps,    dispatch,    options  );}

现在的流程,知道了调用connect()后,会对我们的传入的函数进行一层包装来判断是否依赖于props,随后selectorFactory调用时会根据结果有无变化来判断是否需要重新调用我们的函数

现在就来看核心的高阶函数的实现connectAdvanced

connectAdvanced

import hoistStatics from "hoist-non-react-statics";import React, { useContext, useMemo, useRef, useReducer } from "react";import { isValidElementType, isContextConsumer } from "react-is";import Subscription from "../utils/Subscription";import { useIsomorphicLayoutEffect } from "../utils/useIsomorphicLayoutEffect";import { ReactReduxContext } from "./Context";// 使用useReducer的初始值const EMPTY_ARRAY = [];// 组件不被订阅的值const NO_SUBSCRIPTION_ARRAY = [null, null];//useReducer的reducerfunction storeStateUpdatesReducer(state, action) {  const [, updateCount] = state;  return [action.payload, updateCount + 1];}function useIsomorphicLayoutEffectWithArgs(  effectFunc,  effectArgs,  dependencies) {  useIsomorphicLayoutEffect(() => effectFunc(...effectArgs), dependencies);}function captureWrapperProps(  lastWrapperProps,  lastChildProps,  renderIsScheduled,  wrapperProps,  actualChildProps,  childPropsFromStoreUpdate,  notifyNestedSubs) {  // 存下来用于下次的比较  lastWrapperProps.current = wrapperProps;  lastChildProps.current = actualChildProps;  renderIsScheduled.current = false;  // 如果更新来自store,则清空引用并且通知子级更新  if (childPropsFromStoreUpdate.current) {    childPropsFromStoreUpdate.current = null;    notifyNestedSubs();  }}function subscribeUpdates(  // 是否需要更新  shouldHandleStateChanges,  store,  // Subscription的实例  subscription,  // connect的selector  childPropsSelector,  // 上一次传入组件的props  lastWrapperProps,  // 上一次的props包括组件的props,store props,dispatch props  lastChildProps,  renderIsScheduled,  childPropsFromStoreUpdate,  notifyNestedSubs,  forceComponentUpdateDispatch) {  // 不需要更新  if (!shouldHandleStateChanges) return;  let didUnsubscribe = false;  let lastThrownError = null;  // 每当store的订阅更新传递到此组件都会运行这个回调  const checkForUpdates = () => {    if (didUnsubscribe) {      // redux不能保证在下次dispatch前取消订阅      return;    }    // 新的state    const latestStoreState = store.getState();    let newChildProps, error;    try {      // 获取新的child props      newChildProps = childPropsSelector(        latestStoreState,        lastWrapperProps.current      );    } catch (e) {      error = e;      lastThrownError = e;    }    if (!error) {      lastThrownError = null;    }    // 如果child props没有变就什么都不做    if (newChildProps === lastChildProps.current) {      // 即便自己没变,也要通知订阅自己的子级去检查更新      if (!renderIsScheduled.current) {        notifyNestedSubs();      }    } else {      // 把新的child props存下来,使用ref而不是useState/useReducer是因为我们需要一种方式确定值是否已经被处理      // 如果用useState/useReducer,我们不能在不强制更新的情况下清除值,这不是我们想要的      lastChildProps.current = newChildProps;      childPropsFromStoreUpdate.current = newChildProps;      renderIsScheduled.current = true;      // 如果child props改变或者捕获了错误,这个wrapper component都需要重新渲染      forceComponentUpdateDispatch({        type: "STORE_UPDATED",        payload: {          error        }      });    }  };  // 实际订阅的是最近的父级或者是store  subscription.onStateChange = checkForUpdates;  // 订阅  subscription.trySubscribe();  checkForUpdates();  // 退订  const unsubscribeWrapper = () => {    didUnsubscribe = true;    subscription.tryUnsubscribe();    subscription.onStateChange = null;    if (lastThrownError) {      throw lastThrownError;    }  };  return unsubscribeWrapper;}// useReducer惰性初始化const initStateUpdates = () => [null, 0];export default function connectAdvanced(  selectorFactory,  {  	// 这个函数通过wrapped component的displayName来计算HOC的displayName    // 可能会被wrapper functions例如connect() 覆盖    getDisplayName = name => `ConnectAdvanced(${name})`,  	// 在error messages里显示    methodName = "connectAdvanced",    // REMOVED    renderCountProp = undefined,    // false的时候dispatch里组件也不会更新    shouldHandleStateChanges = true,    // REMOVED    storeKey = "store",    // REMOVED    withRef = false,    // 是否传递ref    forwardRef = false,    // 使用的context consumer    context = ReactReduxContext,    // 其他值将传递给selectorFactory    ...connectOptions  } = {}) {  // ...  // context  const Context = context;  // 实际connect调用的函数,WrappedComponent就是传入的组件  return function wrapWithConnect(WrappedComponent) {    // 传入组件的名字,在react插件上看得到    const wrappedComponentName =      WrappedComponent.displayName || WrappedComponent.name || "Component";    const displayName = getDisplayName(wrappedComponentName);    // 传递给selectorFactory    const selectorFactoryOptions = {      ...connectOptions,      getDisplayName,      methodName,      renderCountProp,      shouldHandleStateChanges,      storeKey,      displayName,      wrappedComponentName,      WrappedComponent    };    // 是否缓存值    const { pure } = connectOptions;		// 封装一下selectorFactory    function createChildSelector(store) {      return selectorFactory(store.dispatch, selectorFactoryOptions);    }    // pure模式下用useMemo,否则直接回调    const usePureOnlyMemo = pure ? useMemo : callback => callback();    // 这是渲染在页面上的组件    function ConnectFunction(props) {      const [propsContext, forwardedRef, wrapperProps] = useMemo(() => {        // 区分传入的props和控制行为的值(forward ref,替换的context实例)        const { forwardedRef, ...wrapperProps } = props;        return [props.context, forwardedRef, wrapperProps];      }, [props]);      // 用组件传入的context还是react redux的context      const ContextToUse = useMemo(() => {        // 缓存应该使用自带的context还是用户传入的context        return propsContext &&          propsContext.Consumer &&          isContextConsumer(<propsContext.Consumer />)          ? propsContext          : Context;      }, [propsContext, Context]);      // 从context里取store和subscription      const contextValue = useContext(ContextToUse);      // store必须在props或者context里存在,所以需要先判断是不是存在      // 我们可以直接把store传给组件      const didStoreComeFromProps =        Boolean(props.store) &&        Boolean(props.store.getState) &&        Boolean(props.store.dispatch);      const didStoreComeFromContext =        Boolean(contextValue) && Boolean(contextValue.store);      // 取出store      const store = didStoreComeFromProps ? props.store : contextValue.store;      const childPropsSelector = useMemo(() => {        // createChildSelector需要store作为参数,在store改变的时候会重新创建        return createChildSelector(store);      }, [store]);      const [subscription, notifyNestedSubs] = useMemo(() => {        // 这时候组件不会随store变化更新        if (!shouldHandleStateChanges)          return NO_SUBSCRIPTION_ARRAY; /* [ null, null ] */        // 如果组件的store是从props里来的,就不需要传入context里的subscription        // 通过这个订阅store来让组件更新        const subscription = new Subscription(          store,          // contextValue.subscription这个值,在Provider根是store的subscription,其余情况都是父级的subscription          // 因为每次connect返回的组件外面包的Provider都使用了新的value          //   <Provider store={store}>          //     <Test4> // store的subscription          //       <Test5 /> // Test4的subscription          //     </Test4>          //     <Test6 /> // store的subscription          //   </Provider>          didStoreComeFromProps ? null : contextValue.subscription        );        // 防止在通知循环中组件被unmount        const notifyNestedSubs = subscription.notifyNestedSubs.bind(          subscription        );        return [subscription, notifyNestedSubs];      }, [store, didStoreComeFromProps, contextValue]);      // 将subscription放入context后的context      // 因为多层connect嵌套会把subscription传给子级connect      const overriddenContextValue = useMemo(() => {        if (didStoreComeFromProps) {          // 如果组件订阅的是从props里的store,我们不希望子级从这个store里获取任何东西          return contextValue;        }        // 否则将当前组件的subscription放入context里,确保子组件在当前组件更新完之前不会更新        return {          ...contextValue,          subscription        };      }, [didStoreComeFromProps, contextValue, subscription]);      // 我们需要在redux store更新的时候强制让包装组件更新      // **正常情况下组件重新的渲染就是因为调用了forceComponentUpdateDispatch,而调用这个就是在订阅的事件中**      const [        [previousStateUpdateResult],        forceComponentUpdateDispatch      ] = useReducer(storeStateUpdatesReducer, EMPTY_ARRAY, initStateUpdates);      // 捕获更新产生的错误      if (previousStateUpdateResult && previousStateUpdateResult.error) {        throw previousStateUpdateResult.error;      }      // 会赋值等于actualChildProps,也就是包括了store,dispatch和传入组件的props      const lastChildProps = useRef();      // 传入组件的props      const lastWrapperProps = useRef(wrapperProps);      const childPropsFromStoreUpdate = useRef();      // 控制是否需要通知子级更新      const renderIsScheduled = useRef(false);      const actualChildProps = usePureOnlyMemo(() => {        // 这次渲染也许是因为redux store更新产生了新props触发的        // 然而,我们也可能在这之后得到父级传入的props        // 如果我们得到一个新的child props,和一个相同的父级传入的props,我们知道我们应该使用新的child props        // 但是,如果父级传入了一个新的props,可能会改变child props,所以我们需要重新计算        // 所以,如果父级的props和上次相同,我们我们会使用从store更新来的新props        if (          childPropsFromStoreUpdate.current &&          wrapperProps === lastWrapperProps.current        ) {          return childPropsFromStoreUpdate.current;        }        return childPropsSelector(store.getState(), wrapperProps);        // 主要因为previousStateUpdateResult的改变,才会重新计算actualChildProps      }, [store, previousStateUpdateResult, wrapperProps]);      // useIsomorphicLayoutEffectWithArgs会根据是服务端还是浏览器端来决定到底调用useEffect还是useLayoutEffect      // 这里主要是初始化值,用做以后更新时的对比      // 还有就是调用自身的notifyNestedSubs,让子组件也更新      useIsomorphicLayoutEffectWithArgs(captureWrapperProps, [        lastWrapperProps,        lastChildProps,        renderIsScheduled,        wrapperProps,        actualChildProps,        childPropsFromStoreUpdate,        notifyNestedSubs      ]);      // 只会在store或者subscription改变时候重新订阅      // 这里主要绑定订阅事件      useIsomorphicLayoutEffectWithArgs(        subscribeUpdates,        [          shouldHandleStateChanges,          store,          subscription,          childPropsSelector,          lastWrapperProps,          lastChildProps,          renderIsScheduled,          childPropsFromStoreUpdate,          notifyNestedSubs,          forceComponentUpdateDispatch        ],        [store, subscription, childPropsSelector]      );      // 下面2个组件用useMemo来优化      const renderedWrappedComponent = useMemo(        () => <WrappedComponent {...actualChildProps} ref={forwardedRef} />,        [forwardedRef, WrappedComponent, actualChildProps]      );      const renderedChild = useMemo(() => {        if (shouldHandleStateChanges) {          // 如果组件订阅了store的更新,我们需要把它的subscription传递给子级          // 也就是同样的context使用不同的值          return (            <ContextToUse.Provider value={overriddenContextValue}>              {renderedWrappedComponent}            </ContextToUse.Provider>          );        }        return renderedWrappedComponent;      }, [ContextToUse, renderedWrappedComponent, overriddenContextValue]);      return renderedChild;    }    // pure时用React.memo优化    const Connect = pure ? React.memo(ConnectFunction) : ConnectFunction;    Connect.WrappedComponent = WrappedComponent;    Connect.displayName = displayName;    // 如果forwardRef开启,则需要把子级的ref传递出来    if (forwardRef) {      const forwarded = React.forwardRef(function forwardConnectRef(        props,        ref      ) {        return <Connect {...props} forwardedRef={ref} />;      });      forwarded.displayName = displayName;      forwarded.WrappedComponent = WrappedComponent;      // 拷贝静态方法并返回      return hoistStatics(forwarded, WrappedComponent);    }    return hoistStatics(Connect, WrappedComponent);  };}

看完整体connectAdvanced后,还是有1个问题没想明白

因为在useSelectorhooks方法里,根本没法传递context,订阅的不都是Provider吗?不就没有了connect()的订阅层级了

希望有大佬能解答这个小小的疑惑


整个react-redux的源码绕来绕去,真的挺复杂的,从第一天一脸懵逼到第七天基本搞明白,甚至自己可以写一个简易版,还是很高兴的