React-Redux原理简析与源码解析

没有看过Redux源码的同窗能够看看个人上一篇文章Redux原理简析与源码解析javascript

本文发布在个人博客java

本文的github仓库
react

自从我学习太高阶组件,你们流传的就是connect()就是一个高阶组件,因此在我长久的认知中,认为connect()仅仅是一个从context中筛选出state,传入给子组件的简单高阶组件git

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

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

原理简述

在分析源码以前,须要先将react-redux的原理简述一下,不然光干看代码仍是会比较懵api

先简单的画一个流程浏览器

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

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

当咱们使用了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/> // 它订阅的Component1 <Component1/> </Provider>
// 当store有更新,Provider通知Component1,Component1通知Component2
复制代码

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

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

更新的原理就是使用咱们传入的mapStateToPropsmapDispatchToProps,结合内置的selectorFactor()来对比stateprops,一旦有改变就强制更新本身,因此咱们传入的WrappedComponent也被强制更新了

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

源码简析

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

Subscription

// Subscriotion.js
const 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.js

function 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件事

  • 订阅reduxsubscribe()事件
  • Subscription实例传入Context方便子级订阅

接下来看看核心connect组件

connect

// connect.js

// 遍历执行函数,将arg做为参数传入,若是有结果则return
function match(arg, factories, name) {
  for (let i = factories.length - 1; i >= 0; i--) {
    const result = factories[i](arg);
    if (result) return result;
  }
  &emsp;// ... 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.js

import { 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默认为true
export 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的reducer
function 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个问题没想明白

  • 为何组件要分层级从上至下订阅,而不是直接订阅storesubscribe

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

但愿有大佬能解答这个小小的疑惑


整个react-redux的源码绕来绕去,真的挺复杂的,若是有疑问你们能够互相交流

从第一天一脸懵逼到第七天基本搞明白,甚至写了一个简易版,仍是很高兴的

最后,祝你们身体健康,工做顺利!

欢迎你们关注个人公众号~

相关文章
相关标签/搜索