Redux 的介绍已经在之前的文章说明了,这里主要讲的是 React Redux,随着 React Hooks 的推进,Hooks 的代码更贴近 React 的生态,无论在用法还是代码体积都更加友善。
Commons
无论是使用 Hooks 还是 Connect,都需要用到的公共部分
Subscription
import { getBatch } from './batch'
// NOTE:初始化listeners
const CLEARED = null
const nullListeners = { notify() {} }
function createListenerCollection() {
// NOTE:拿到reactdom的state合并方法,防止订阅过多的setState,造成性能问题
const batch = getBatch()
let current = []
let next = []
return {
clear() {
next = CLEARED
current = CLEARED
},
// NOTE:通知执行订阅的方法
notify() {
const listeners = (current = next)
batch(() => {
for (let i = 0; i < listeners.length; i++) {
listeners[i]()
}
})
},
get() {
return next
},
// NOTE:注册监听
subscribe(listener) {
let isSubscribed = true
// NOTE:确保next获取的是正确的current而不是引用
if (next === current) next = current.slice()
next.push(listener)
return function unsubscribe() {
if (!isSubscribed || current === CLEARED) return
isSubscribed = false
if (next === current) next = current.slice()
next.splice(next.indexOf(listener), 1)
}
},
}
}
// NOTE:new Subscription(store),parentSub为undefined
export default class Subscription {
constructor(store, parentSub) {
this.store = store
this.parentSub = parentSub
this.unsubscribe = null
this.listeners = nullListeners
this.handleChangeWrapper = this.handleChangeWrapper.bind(this)
}
// NOTE:增加嵌套订阅
addNestedSub(listener) {
this.trySubscribe()
return this.listeners.subscribe(listener)
}
notifyNestedSubs() {
this.listeners.notify()
}
// NOTE:store收到dispatch会执行nextListeners订阅组,这里provider会将该函数注册到nextListeners中
handleChangeWrapper() {
if (this.onStateChange) {
this.onStateChange()
}
}
isSubscribed() {
return Boolean(this.unsubscribe)
}
// NOTE:没有订阅,就判断parentSub,如果有则帮到parentSub的订阅组中,否则绑到当前store
trySubscribe() {
if (!this.unsubscribe) {
this.unsubscribe = this.parentSub
? this.parentSub.addNestedSub(this.handleChangeWrapper)
: this.store.subscribe(this.handleChangeWrapper) //NOTE:给store的nextListeners增加一个订阅的方法
this.listeners = createListenerCollection()
}
}
tryUnsubscribe() {
if (this.unsubscribe) {
this.unsubscribe()
this.unsubscribe = null
this.listeners.clear()
this.listeners = nullListeners
}
}
}
Subscription(工具类),不存在流程等问题,主要作用就是用来生成一个可以订阅的类,重点可以了解实现的逻辑
- 可以用增加和取消订阅
- 可以给当前 Store 或者一个订阅的类增加订阅的事件;
- 可以初始化和通知全部已订阅的事件;
Provider
import React, { useMemo, useEffect } from 'react'
import { ReactReduxContext } from './Context'
import Subscription from '../utils/Subscription'
function Provider({ store, context, children }) {
const contextValue = useMemo(() => {
const subscription = new Subscription(store) // 将Store生成一个订阅实例
// NOTE:将实例的通知订阅组的方法放到stateChange来处理
subscription.onStateChange = subscription.notifyNestedSubs
return { store, subscription }
}, [store])
// NOTE:用来保存Store的State
const previousState = useMemo(() => store.getState(), [store])
useEffect(() => {
const { subscription } = contextValue
subscription.trySubscribe() // 初始化全部订阅
// NOTE:state有改变时,执行全部provider订阅的事件
if (previousState !== store.getState()) {
subscription.notifyNestedSubs()
}
return () => {
// NOTE:解绑和重制onStateChange,因为redux的store那边注册了该方法
subscription.tryUnsubscribe()
subscription.onStateChange = null
}
}, [contextValue, previousState])
const Context = context || ReactReduxContext
return <Context.Provider value={contextValue}>{children}</Context.Provider>
}
export default Provider
Provide 的作用
- 先 new Subscription,这个是 Store 的 Subscription
- 将 Subscription 的方法等注册,主要用来实现订阅和更新
- 创建一个 Provider,用来传递和存储 Store 和 Subscription
Context
import React from 'react'
// NOTE:生成一个Context,用作全局的store,多处地方会使用,如provider,还有一些useStore等
export const ReactReduxContext = React.createContext(null)
export default ReactReduxContext
shallowEqual
const hasOwn = Object.prototype.hasOwnProperty
function is(x, y) {
if (x === y) {
return x !== 0 || y !== 0 || 1 / x === 1 / y
} else {
return x !== x && y !== y
}
}
// NOTE:浅对比
export default function shallowEqual(objA, objB) {
if (is(objA, objB)) return true
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false
}
const keysA = Object.keys(objA)
const keysB = Object.keys(objB)
if (keysA.length !== keysB.length) return false
for (let i = 0; i < keysA.length; i++) {
if (!hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false
}
}
return true
}
Hooks
useStore
import { useContext } from 'react'
import { ReactReduxContext } from '../components/Context'
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
// NOTE:有传入context则使用传入,否则使用useStore里的store,并且返回store
export function createStoreHook(context = ReactReduxContext) {
const useReduxContext =
context === ReactReduxContext
? useDefaultReduxContext
: () => useContext(context)
return function useStore() {
const { store } = useReduxContext()
return store
}
}
export const useStore = createStoreHook()
useDispatch
import { ReactReduxContext } from '../components/Context'
import { useStore as useDefaultStore, createStoreHook } from './useStore'
// NOTE:和useStore同样逻辑,如有传入context则使用传入,否则使用useStore里的store,并且返回store的dispatch
export function createDispatchHook(context = ReactReduxContext) {
const useStore =
context === ReactReduxContext ? useDefaultStore : createStoreHook(context)
return function useDispatch() {
const store = useStore()
return store.dispatch
}
}
export const useDispatch = createDispatchHook()
useReduxContext
import { useContext } from 'react'
import { ReactReduxContext } from '../components/Context'
// NOTE:获取ReduxdContext的Value,一般用来拿Store
export function useReduxContext() {
const contextValue = useContext(ReactReduxContext)
return contextValue
}
useSelector
import {
useReducer,
useRef,
useEffect,
useMemo,
useLayoutEffect,
useContext,
} from 'react'
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
import Subscription from '../utils/Subscription'
import { ReactReduxContext } from '../components/Context'
// NOTE:使用同构LayoutEffect
const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect
// NOTE:浅对比前后两个store的值
const refEquality = (a, b) => a === b
function useSelectorWithStoreAndSubscription(
selector, //NOTE:state => state.test
equalityFn,
store,
contextSub
) {
// NOTE:定义一个useReducer,用来进行update
const [, forceRender] = useReducer(s => s + 1, 0)
// NOTE:生成一个Subscription的实例
const subscription = useMemo(
() => new Subscription(store, contextSub),
[store, contextSub]
)
// NOTE:用来存储state和error信息
const latestSubscriptionCallbackError = useRef()
const latestSelector = useRef()
const latestSelectedState = useRef()
let selectedState
try {
// NOTE:如果两个selector的不相等,则重新处理,否则按上一次
if (
selector !== latestSelector.current ||
latestSubscriptionCallbackError.current
) {
selectedState = selector(store.getState())
} else {
selectedState = latestSelectedState.current
}
} catch (err) {
// NOTE:在获取Store的时候,如果有问题就抛出异常
throw new Error()
}
// NOTE:在执行effect时,将最新的selector存储
useIsomorphicLayoutEffect(() => {
latestSelector.current = selector
latestSelectedState.current = selectedState
latestSubscriptionCallbackError.current = undefined
})
// NOTE:当Store或subscription有改变的话,直接执行checkForUpdates
useIsomorphicLayoutEffect(() => {
// NOTE:对比前后selectState的值,全等则不做处理,有改变则将最新的值存储,并执行update的订阅
function checkForUpdates() {
try {
const newSelectedState = latestSelector.current(store.getState())
if (equalityFn(newSelectedState, latestSelectedState.current)) {
return
}
latestSelectedState.current = newSelectedState
} catch (err) {
latestSubscriptionCallbackError.current = err
}
// NOTE:update,将最新的selectedState返回
forceRender({})
}
// NOTE:在原先Store的subscription上增加订阅了checkForUpdates
subscription.onStateChange = checkForUpdates
subscription.trySubscribe()
/*
NOTE:个人疑惑,一开始因为依赖项是store和subscription,那样应该store一变就执行这个effect,
后面想清楚,store, subscription如果不是完全的改变是不会触发,因为指针并未改变
所以后面只是subscription去触发checkForUpdates,而不是effect
*/
checkForUpdates()
return () => subscription.tryUnsubscribe()
}, [store, subscription])
return selectedState
}
// NOTE:与其他Hooks同理
export function createSelectorHook(context = ReactReduxContext) {
const useReduxContext =
context === ReactReduxContext
? useDefaultReduxContext
: () => useContext(context)
return function useSelector(selector, equalityFn = refEquality) {
// NOTE:关键点,获取当前Store的subscription和context的信息
const { store, subscription: contextSub } = useReduxContext()
// NOTE:等于在对当前Store进行了扩展和订阅
return useSelectorWithStoreAndSubscription(
selector,
equalityFn,
store,
contextSub
)
}
}
export const useSelector = createSelectorHook()
useSelector 的执行流程:
- 先定义使用的 effect 和一些判断的工具函数
- 定义 reducer 用来进行 state 的更新
- new Subscription,这个是 useSelector 的 Subscription,useSelector 用在组件里面,一般就是当前组件的 Subscription
- 定义用来存储 state 和 error 等的变量
- 处理好存储 state 和 error 的场景
- 注册订阅和更新
- 返回一个处理好 Store 的 useSelector
Hooks 的 React Redux 对比以前用 Connect 简单不少,重点只要看 Provider、useSelector 和 Subscription,其他的 hooks 和 context,都是存储和创建 store 的 context,hooks 就直接使用 store 的 contextValue。
与 Redux 结合的 Flow
Example
import React, { useCallback } from 'react'
import { useDispatch, useReduxContext, useSelector } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
const { store } = useReduxContext()
const storeSate = useStore()
const counter = useSelector(state => state.counter)
const increaseCounter = useCallback(
() => dispatch({ type: 'increase-counter' }),
[]
)
return (
<div>
<span>{value}</span>
<div>{store.getState()}</div>
<div>{storeSate.getState()}</div>
<button onClick={increaseCounter}>Increase {counter}</button>
</div>
)
}
Connect()
在上已经说明 Hooks 的部分,下面主要是 Connect 部份,只说明 Connect 部份的主要代码,一些 Commons 的 Provider 就不重复说明了。
connect
import connectAdvanced from '../components/connectAdvanced'
import shallowEqual from '../utils/shallowEqual'
import defaultMapDispatchToPropsFactories from './mapDispatchToProps'
import defaultMapStateToPropsFactories from './mapStateToProps'
import defaultMergePropsFactories from './mergeProps'
import defaultSelectorFactory from './selectorFactory'
// NOTE:将参数全部放到工厂函数中执行,拿回工厂函数的返回值
function match(arg, factories, name) {
for (let i = factories.length - 1; i >= 0; i--) {
const result = factories[i](arg)
if (result) return result
}
}
/*
createConnect返回了一个connect,它主要将
mapStateToPropsFactories
mapDispatchToPropsFactories
mergePropsFactories
selectorFactory
等工厂函数设置好
*/
export function createConnect({
connectHOC = connectAdvanced,
mapStateToPropsFactories = defaultMapStateToPropsFactories,
mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
mergePropsFactories = defaultMergePropsFactories,
selectorFactory = defaultSelectorFactory,
} = {}) {
/*
connect主要是返回了connectHOC,一个HOC组件用来包裹用户的components
主要作用是接收用户传递下来的 mapStateToProps、mapDispatchToProps、mergeProps、options
并且将mapStateToProps、mapDispatchToProps、mergeProps通过工厂函数进行处理,传递给HOC
*/
return function connect(
mapStateToProps,
mapDispatchToProps,
mergeProps,
{
pure = true,
areStatesEqual = strictEqual,
areOwnPropsEqual = shallowEqual,
areStatePropsEqual = shallowEqual,
areMergedPropsEqual = shallowEqual,
...extraOptions
} = {}
) {
/* NOTE:initMapStateToProps、initMapDispatchToProps、initMergeProps
三个方法基本相同,主要用于借助工厂函数合并props、封装actions等,具体的可以看mapStateToProps,这里不对每个都进行过一遍
然后return 一个function
*/
const initMapStateToProps = match(
mapStateToProps,
mapStateToPropsFactories,
'mapStateToProps'
)
const initMapDispatchToProps = match(
mapDispatchToProps,
mapDispatchToPropsFactories,
'mapDispatchToProps'
)
const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')
return connectHOC(selectorFactory, {
methodName: 'connect', // 在error的时候会进行显示
getDisplayName: name => `Connect(${name})`, // 获取组件的displayName.
shouldHandleStateChanges: Boolean(mapStateToProps), // mapStateToProps为true,connect的组件会订阅state的改变
// 需要用到的一些props参数和equal的参数
initMapStateToProps,
initMapDispatchToProps,
initMergeProps,
pure,
areStatesEqual,
areOwnPropsEqual,
areStatePropsEqual,
areMergedPropsEqual,
// 其他的用户设置参数设置
...extraOptions,
})
}
}
export default createConnect()
connectAdvanced
/*
NOTE:原始组件的所有静态方法全部拷贝给新组件
class Demo extends React.Component{
render(){
return (<div>Demo</div>)
}
}
Demo.test = () => console.log('test')
在被高阶组件包裹后会失去test,const NewDemo = Hoc(Demo)
typeof NewDemo.test === 'undefined' // true
hoist-non-react-statics会自动处理这种绑定: https://reactjs.org/docs/higher-order-components.html#static-methods-must-be-copied-over
*/
import hoistStatics from 'hoist-non-react-statics'
import React, {
useContext,
useMemo,
useEffect,
useLayoutEffect,
useRef,
useReducer,
} from 'react'
import { isValidElementType, isContextConsumer } from 'react-is'
import Subscription from '../utils/Subscription'
import { ReactReduxContext } from './Context'
const EMPTY_ARRAY = [] // 不进行更新时的state设置
const NO_SUBSCRIPTION_ARRAY = [null, null] // 不进行订阅的Subscription设置
// NOTE:创建一个Reducer,每次调用就count + 1,并将payload(latestStoreState,error)等存起来
function storeStateUpdatesReducer(state, action) {
const [, updateCount] = state
return [action.payload, updateCount + 1]
}
// NOTE:惰性去初始化Reducer的state
const initStateUpdates = () => [null, 0]
// NOTE:判断使用Effect还是LayoutEffect,如果同构则使用useLayoutEffect
const useIsomorphicLayoutEffect =
typeof window?.document?.createElement !== 'undefined'
? useLayoutEffect
: useEffect
// NOTE:connectAdvanced就是返回一个function组件,wrapWithConnect,并且设置好参数信息和验证
export default function connectAdvanced(
/*
selectorFactory也即是finalPropsSelectorFactory,主要用来处理props
通过options的pure来决定使用的合并props的处理方式
pure的时候,可以直接拿结果,如果props没有改变,connectAdvanced shouldComponentUpdate可以返回false,不更新
非pure的时候,直接拿新的props对象,进行更新
*/
selectorFactory,
{
getDisplayName = name => `ConnectAdvanced(${name})`, // 获取组件的displayName
methodName = 'connectAdvanced', // 用来显示错误信息
renderCountProp = undefined, // 已经被废弃
shouldHandleStateChanges = true, // 即是Boolean(mapStateToProps)
storeKey = 'store', // 已经被废弃
withRef = false, // 已经被废弃
forwardRef = false, // 在组件中是否有使用forwardRef,需要用户进行设置
context = ReactReduxContext, // 默认值ReactReduxContext,即最顶层的provider,一般存放store
...connectOptions // 另外一些用户设置的options
} = {}
) {
const Context = context
// NOTE:包裹的组件,connect()(WrappedComponent: <Test />),
return function wrapWithConnect(WrappedComponent) {
// NOTE:处理好组件名、还有一些options的设置
const wrappedComponentName =
WrappedComponent.displayName || WrappedComponent.name || 'Component'
const displayName = getDisplayName(wrappedComponentName)
const selectorFactoryOptions = {
...connectOptions,
getDisplayName,
methodName,
renderCountProp,
shouldHandleStateChanges,
storeKey,
displayName,
wrappedComponentName,
WrappedComponent,
}
const { pure } = connectOptions
// NOTE:用selectorFactory函数生成新的Props
function createChildSelector(store) {
return selectorFactory(store.dispatch, selectorFactoryOptions)
}
// NOTE:如果是Pure会进行优化处理,具体可看Memo
const usePureOnlyMemo = pure ? useMemo : callback => callback()
// NOTE:最重要看这个,按流程看,我们先从return的东西反过来看
function ConnectFunction(props) {
/**
* NOTE:流程3 都是由props传下来的信息,一般业务情况下也没有传propsContext, forwardedRef
* wrapperProps就是connect的组件,本身是否有传递的props
* */
const [propsContext, forwardedRef, wrapperProps] = useMemo(() => {
const { forwardedRef, ...wrapperProps } = props
return [props.context, forwardedRef, wrapperProps]
}, [props])
/**
* NOTE:流程2 获取使用的Context,
* 判断是使用propsContext(props,最近一层)还是Context(全局,最顶层)
* 一般情况下都是使用Context(全局,最顶层)
* */
const ContextToUse = useMemo(() => {
return propsContext &&
propsContext.Consumer &&
isContextConsumer(<propsContext.Consumer />)
? propsContext
: Context
}, [propsContext, Context])
// NOTE:生成contextValue
const contextValue = useContext(ContextToUse)
// NOTE:store必须是来自props,来自上下文context
const didStoreComeFromProps = Boolean(props.store)
const didStoreComeFromContext =
Boolean(contextValue) && Boolean(contextValue.store)
const store = props.store || contextValue.store
// NOTE:用Store传入生成props
const childPropsSelector = useMemo(() => {
return createChildSelector(store)
}, [store])
// NOTE:流程5 subscription,就是Subscription,具体可以看Subscription.js,它可以订阅和通知执行所有的订阅事件等
const [subscription, notifyNestedSubs] = useMemo(() => {
// NOTE:如果不需要处理stateChanges,则不增加订阅
if (!shouldHandleStateChanges) return NO_SUBSCRIPTION_ARRAY
// NOTE:生成一个新的Subscription,并且传入store
const subscription = new Subscription(
store,
didStoreComeFromProps ? null : contextValue.subscription // 如props传下来的store,则订阅到该store里,否则增加到最顶层的provider的订阅中
)
// NOTE:暴露出notifyNestedSubs,并将subscription的notifyNestedSubs,bind到当前subscription
const notifyNestedSubs =
subscription.notifyNestedSubs.bind(subscription)
return [subscription, notifyNestedSubs]
}, [store, didStoreComeFromProps, contextValue])
/**
* 流程4 判断是否需要返回subscription订阅
*/
const overriddenContextValue = useMemo(() => {
/**
* NOTE:如果store从props传下来,那就返回contextValue,否则需要加上subscription
* 大多数都没有从props去传递store,更多是直接connect
* 所以主要还是加上了订阅
*/
if (didStoreComeFromProps) {
return contextValue
}
return {
...contextValue,
subscription,
}
}, [didStoreComeFromProps, contextValue, subscription])
// NOTE:应用了一个Reducer,用于更新,具体用法可以看官方useReducer,基本与Reducer相似
const [
[previousStateUpdateResult], // [previousStateUpdateResult] = [action.payload, updateCount + 1]
forceComponentUpdateDispatch,
] = useReducer(storeStateUpdatesReducer, EMPTY_ARRAY, initStateUpdates)
// NOTE:判断更新结果是否有error,有则抛出error
if (previousStateUpdateResult && previousStateUpdateResult.error) {
throw previousStateUpdateResult.error
}
// NOTE:初始化定义一些参数,用来保存数据和状态
const lastChildProps = useRef() // 最后一次child的props,包含、dispatch、router等有中间件和父组件的props
const lastWrapperProps = useRef(wrapperProps) // 最后一次渲染组件的Props,一个是父组件传的
const childPropsFromStoreUpdate = useRef()
// NOTE:是否Render已经被预约,防止造成多render
const renderIsScheduled = useRef(false)
// NOTE:获取真实的child的props
const actualChildProps = usePureOnlyMemo(() => {
// NOTE:如果是从Store里面更新,并且props和上一个props相等,直接返回props,否则就创建一个新的props
if (
childPropsFromStoreUpdate.current &&
wrapperProps === lastWrapperProps.current
) {
return childPropsFromStoreUpdate.current
}
return childPropsSelector(store.getState(), wrapperProps)
}, [store, previousStateUpdateResult, wrapperProps])
/**
* NOTE:流程6 当流程跑完我们按顺序看一下两个Effect
* 该effect每次render都执行,作用在存储props,然后通知所有的订阅
*/
useIsomorphicLayoutEffect(() => {
// NOTE:一有更新就将props还有actualChildProps存起来
lastWrapperProps.current = wrapperProps
lastChildProps.current = actualChildProps
renderIsScheduled.current = false
// NOTE:如果props从Store里面更新,就重置childPropsFromStoreUpdate和执行全部订阅
if (childPropsFromStoreUpdate.current) {
childPropsFromStoreUpdate.current = null
notifyNestedSubs()
}
})
// NOTE:流程7 该Effect使用来处理是否更新
useIsomorphicLayoutEffect(() => {
if (!shouldHandleStateChanges) return
// NOTE:判断是否已经取消订阅
let didUnsubscribe = false
// NOTE:判断是否有抛出error
let lastThrownError = null
// NOTE:和名字一样,作为判断是否要Update处理
const checkForUpdates = () => {
//NOTE:取消订阅不做处理
if (didUnsubscribe) {
return
}
// NOTE:获取store的整个状态
const latestStoreState = store.getState()
let newChildProps, error
try {
// NOTE:将整个store的state和当前组件的WrapperProps生成一个最新的childProps
newChildProps = childPropsSelector(
latestStoreState,
lastWrapperProps.current
)
} catch (e) {
// NOTE:看是否生成的过程中出错
error = e
lastThrownError = e
}
if (!error) {
lastThrownError = null
}
// NOTE:生成的最新childProps和存起来的lastChildProps是否相等,并且会看render是否已经在进行
if (newChildProps === lastChildProps.current) {
if (!renderIsScheduled.current) {
/**
* NOTE:前后props相等而且不在render中,执行所有已经订阅的方法
* notifyNestedSubs是new Subscription,后续的subscription.trySubscribe(),是将checkForUpdates绑定到
* 顶层的subscription
* 所以该方法不会造成死循环
* */
notifyNestedSubs()
}
} else {
// NOTE:当前后props不相等,则将新的props存起来,并将renderIsScheduled设置为true,做好要update的准备
lastChildProps.current = newChildProps
childPropsFromStoreUpdate.current = newChildProps
renderIsScheduled.current = true
// NOTE:通过dispatch去update,刷新了当前这个Connect()(Components)
forceComponentUpdateDispatch({
type: 'STORE_UPDATED',
payload: {
latestStoreState,
error,
},
})
}
}
// NOTE:将checkForUpdates这个方法放到onStateChange,然后trySubscribe去初始化注册,将onStateChange注册到store的订阅中
subscription.onStateChange = checkForUpdates
subscription.trySubscribe()
// NOTE:首次的时候进行一次检查更新,确保准确性
checkForUpdates()
// NOTE:解绑订阅的事件
const unsubscribeWrapper = () => {
didUnsubscribe = true
subscription.tryUnsubscribe()
subscription.onStateChange = null
if (lastThrownError) {
throw lastThrownError
}
}
return unsubscribeWrapper
}, [store, subscription, childPropsSelector])
// NOTE:对组件进行memo优化
const renderedWrappedComponent = useMemo(
() => <WrappedComponent {...actualChildProps} ref={forwardedRef} />,
[forwardedRef, WrappedComponent, actualChildProps]
)
// NOTE:流程1-整个函数的返回,加上memo进行优化
const renderedChild = useMemo(() => {
/*
NOTE:流程1.1-判断是否要处理state的改变
即是Boolean(mapStateToProps),如connect()(),则不需要处理state改变
不需要处理的直接将组件render,需要的则包裹一个Provider
*/
if (shouldHandleStateChanges) {
return (
<ContextToUse.Provider value={overriddenContextValue}>
{renderedWrappedComponent}
</ContextToUse.Provider>
)
}
return renderedWrappedComponent
}, [ContextToUse, renderedWrappedComponent, overriddenContextValue])
return renderedChild
}
// NOTE:Connect的参数配置,pure就加上memo,Connect即是ConnectFunction
const Connect = pure ? React.memo(ConnectFunction) : ConnectFunction
Connect.WrappedComponent = WrappedComponent
Connect.displayName = displayName
return hoistStatics(Connect, WrappedComponent)
}
}
Flow
Conclusion
对比 Hooks 的 useSelector 代码,Connect 的代码更加复杂,需要用工厂函数 (initMapStateToProps、initMapDispatchToProps、initMergeProps) 去合并 props、封装 actions,而且因为代码量更多,它的代码体积也会更大,与 React 未来的新特性结合也没有那么好,所以建议使用 Hooks,方便维护和使用新 React 特性。