没有看过Redux源码的同学可以看看我的上一篇文章Redux原理简析与源码解析
本文发布在我的博客
本文的github仓库
自从我学习过高阶组件,大家流传的就是connect()
就是一个高阶组件,所以在我长久的认知中,认为connect()
仅仅是一个从context
中筛选出state
,传入给子组件的简单高阶组件
我个人用redux
是比较少的,自从hooks
出了以后,就经常用useContext
+useReducer
来实现简单的redux
的功能,所以也认为在react-redux
里,connect()
会像useReducer
一样,数据重新渲染由React
控制
结论当然不是这样,并且connect()
组件也不简单,可以说在我打开源码的第一时间傻眼了,下面就来好好的分析下react-redux
在分析源码之前,需要先将react-redux
的原理简述一下,否则光干看代码还是会比较懵
先简单的画一个流程
react-redux
的核心机制是通知订阅模式,源码中有一个Subscription
类,它的作用主要是订阅父级的更新和通知子级的更新,也就是它既可以订阅别人,别人也可以订阅它,同时可以通知订阅它的Subscription
最外层的Provider
组件的Context
里包含了的store
(也就是我们传入的)和生成的Subscription
实例,它的Subscription
实例订阅的则是redux
的subscrib()
当我们使用了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
的源码绕来绕去,真的挺复杂的,如果有疑问大家可以互相交流
从第一天一脸懵逼到第七天基本搞明白,甚至写了一个简易版,还是很高兴的
最后,祝大家身体健康,工作顺利!
欢迎大家关注我的公众号~