没有看过Redux源码的同窗能够看看个人上一篇文章Redux原理简析与源码解析javascript
本文发布在个人博客java
本文的github仓库
react
自从我学习太高阶组件,你们流传的就是connect()
就是一个高阶组件,因此在我长久的认知中,认为connect()
仅仅是一个从context
中筛选出state
,传入给子组件的简单高阶组件git
我我的用redux
是比较少的,自从hooks
出了之后,就常常用useContext
+useReducer
来实现简单的redux
的功能,因此也认为在react-redux
里,connect()
会像useReducer
同样,数据从新渲染由React
控制github
结论固然不是这样,而且connect()
组件也不简单,能够说在我打开源码的第一时间傻眼了,下面就来好好的分析下react-redux
redux
在分析源码以前,须要先将react-redux
的原理简述一下,不然光干看代码仍是会比较懵api
先简单的画一个流程浏览器
react-redux
的核心机制是通知订阅模式,源码中有一个Subscription
类,它的做用主要是订阅父级的更新和通知子级的更新,也就是它既能够订阅别人,别人也能够订阅它,同时能够通知订阅它的Subscription
缓存
最外层的Provider
组件的Context
里包含了的store
(也就是咱们传入的)和生成的Subscription
实例,它的Subscription
实例订阅的则是redux
的subscrib()
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
来更新它们
更新的原理就是使用咱们传入的mapStateToProps
和mapDispatchToProps
,结合内置的selectorFactor()
来对比state
和props
,一旦有改变就强制更新本身,因此咱们传入的WrappedComponent
也被强制更新了
原理简单来说就是这样,下面来看源码
在顺着流程分析以前,先看看贯通整个react-redux
更新流程的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
里实现了什么
// 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件事
redux
的subscribe()
事件Subscription
实例传入Context
方便子级订阅接下来看看核心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;
}
 // ... 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()
,可是基本用不上
能够看到咱们传入的mapStateToProps
,mapDispatchToProps
、mergeProps
其实是经过了一个match()
函数的包装校验
这里就以mapStateToPropsFactories
也就是defaultMapStateToPropsFactories
为例
// 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.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
铺路,它的做用是根据state
或props
的变化,判断是否须要调用咱们的mapStateToProps
、mapDispatchToProps
和mergeProps
返回新的数据
下面看看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
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个问题没想明白
store
的subscribe
?由于在useSelector
的hooks
方法里,没传递context
,订阅的不都是Provider
吗?不就没有了connect()
的订阅层级了
但愿有大佬能解答这个小小的疑惑
整个react-redux
的源码绕来绕去,真的挺复杂的,若是有疑问你们能够互相交流
从第一天一脸懵逼到第七天基本搞明白,甚至写了一个简易版,仍是很高兴的
最后,祝你们身体健康,工做顺利!
欢迎你们关注个人公众号~