5.1💥 connect 深度源码解析:React-Redux 的真实内核是什么?(番外篇)

78 阅读3分钟

✍️ 技术解析 + Redux 作者角度

📚 底层核心文件:connectAdvanced.tsSubscription.tsReactReduxContext.tsx

🎯 目标:你将彻底明白 connect 是如何“优雅注入状态 + 高效更新组件”的


✅ 第一步:connect ≠ connectAdvanced,后者才是真核

react-redux 中,connect 只是个“糖”:

export function connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  options
) {
  return connectAdvanced(selectorFactory, options)
}

它调用的是底层函数:

connectAdvanced(selectorFactory, options)

其中 selectorFactory 是生成 props 的逻辑工厂,而 connectAdvanced 则管理:

  • store 注入
  • props 合成
  • state 对比
  • context 绑定
  • 订阅更新

🧠 第二步:connectAdvanced 源码结构全图

function connectAdvanced(selectorFactory, options) {
  return function wrapWithConnect(WrappedComponent) {
    function ConnectFunction(props) {
      const contextValue = useContext(ReactReduxContext)
      const store = contextValue.store

      const subscription = useMemo(() => new Subscription(store), [store])
      const childPropsSelector = useMemo(() => selectorFactory(store.dispatch, options), [store])

      const [childProps, forceUpdate] = useReducer(c => c + 1, 0)

      useLayoutEffect(() => {
        subscription.subscribe(() => {
          const newProps = childPropsSelector(store.getState(), props)
          if (!shallowEqual(newProps, childProps)) {
            forceUpdate()
          }
        })
        return () => subscription.unsubscribe()
      }, [store])

      const mergedProps = { ...props, ...childProps }
      return <WrappedComponent {...mergedProps} />
    }

    return ConnectFunction
  }
}

关键结构说明:

名称作用说明
ReactReduxContext提供全局 Redux store 引用
Subscription组件级订阅封装(隔离更新链)
selectorFactory用于生成 mapStateToProps + mapDispatchToProps + mergeProps
useLayoutEffect保证同步订阅响应,避免跳帧
useReducer + forceUpdate触发组件重渲染的技巧

🔄 第三步:为什么不用 useState,而是 useReducer + forceUpdate?

因为我们不关心“某个新状态”,而只想「强制组件 render 一次」。

const [, forceUpdate] = useReducer(c => c + 1, 0)

等价于:

function update() {
  renderCount += 1
  rerender()
}

这是目前 React Hook 中触发重渲染的最佳方式之一,避免污染状态结构


🧩 第四步:Subscription 是如何隔离更新链的?

源码文件:Subscription.ts

class Subscription {
  constructor(store, parentSub) {
    this.store = store
    this.parentSub = parentSub
    this.listeners = []
  }

  subscribe(listener) {
    this.listeners.push(listener)
    return () => this.unsubscribe(listener)
  }

  notify() {
    for (const l of this.listeners) {
      l()
    }
  }
}

Redux 的订阅不是在全局 store 上注册的,而是每个 connect 创建一个 独立的 Subscription 实例,挂载在组件自身上。

这有什么好处?

  • 🚀 只更新自己;
  • 🧩 支持嵌套组件 connect(子组件订阅不会被父组件遮挡);
  • 💡 组件卸载自动解除订阅。

🧪 第五步:性能优化全在 selectorFactory + shallowEqual

selectorFactory 的核心返回:

(state, ownProps) => mergedProps

它是这样组合的:

const propsFromState = mapStateToProps(state, ownProps)
const propsFromDispatch = mapDispatchToProps(dispatch, ownProps)
const mergedProps = mergeProps(propsFromState, propsFromDispatch, ownProps)

然后在每次 state 更新后:

const newProps = childPropsSelector(store.getState(), ownProps)
if (!shallowEqual(newProps, prevProps)) {
  rerender()
}

也就是说,只要你传入的 mapStateToProps 结果没变,组件根本不会更新。

这就是 Redux 组件相比 MobX 更可控的地方:更新只发生在“你真的关心的 state 改了”的时候。


🔎 第六步:v7 / 未来版本支持 Concurrent 模式

React 新架构(如 Concurrent + Server Component)要求 state 更新是「延迟合并 + 不阻塞渲染」,这对 Redux 的订阅机制提出新挑战。

我们对 react-redux 做了:

  • ✅ Context 分片更新支持
  • ✅ useSyncExternalStore 支持(v8 完整兼容)
  • ✅ 多组件共享 selector 缓存(useSelector with memo)

目标是:零额外更新、零冗余 re-render、天然类型推导


📦 总结:connect 是结构性封装,而非逻辑注入

机制目的
Context + Subscription注入 store + 局部更新
selectorFactory动态构建 props
useReducer 强制更新避免无意义状态依赖
shallowEqual精准对比,避免无效重渲染
connectAdvanced统一封装所有生命周期与性能逻辑

Redux 是同步状态容器,React 是异步视图引擎,connect 是这两者的完美适配器


⏭️ 下一篇预告

下一篇我们将进入Redux 架构最佳实践层面,站在真实项目的视角回答:

Reducer 怎么拆分才合理? 大型系统如何组织 action? Redux Toolkit 如何减少心智负担?

敬请期待: 第6篇:《Redux 架构最佳实践:如何构建可维护、可扩展的状态体系》