沒有看過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
的源碼繞來繞去,真的挺複雜的,若是有疑問你們能夠互相交流
從第一天一臉懵逼到第七天基本搞明白,甚至寫了一個簡易版,仍是很高興的
最後,祝你們身體健康,工做順利!
歡迎你們關注個人公衆號~