✍️ 模拟 Dan Abramov 视角 + React-Redux 作者 Mark Erikson 的底层解构
🎯 主线:从 Context 到高阶组件,揭秘 Redux 与 React 的桥梁
🧠 关键词:connect、useSelector、性能优化、Context 隔离、订阅模型
🧩 起点问题:组件如何感知 Redux 状态?
Redux 提供了状态管理容器没错,但你不可能手动去:
store.subscribe(...)
store.getState()
每次组件更新都去比对全量 state?那性能爆炸。
所以我们要解决两个关键点:
- 如何让组件「自动感知」状态变化?
- 如何让组件「只更新自己关注的那一小块」?
这就是我们写出 react-redux 的初衷。
🧱 第一代实现:connect(mapStateToProps)
使用方式:
const mapState = state => ({
count: state.counter.value
})
const mapDispatch = {
increment
}
export default connect(mapState, mapDispatch)(MyComponent)
背后的本质是一个 高阶组件(HOC):
function connect(mapStateToProps, mapDispatchToProps) {
return function wrapWithConnect(WrappedComponent) {
return class ConnectedComponent extends React.Component {
// 订阅 store、取数据、props 合成
}
}
}
它会在组件挂载时订阅 Redux,并在状态变更时通过 setState() 触发更新。
🌐 关键依赖:React Context 传递 store
在 Redux 应用的根组件中,你会看到这样一段:
<Provider store={store}>
<App />
</Provider>
这一步非常重要,它将 Redux store 通过 React Context 向下传递。
实现原理(简化):
const StoreContext = React.createContext()
function Provider({ store, children }) {
return (
<StoreContext.Provider value={store}>
{children}
</StoreContext.Provider>
)
}
所以,connect 本质上是:
- 拿到
store(来自Context); - 调用
store.getState()+mapStateToProps; - 自动调用
store.subscribe()注册回调; - 在
dispatch后触发组件重新 render。
🔥 核心挑战一:如何避免所有组件都更新?
Redux 是单状态树,状态变动后如果每个组件都订阅 store.getState() 就全体更新,性能灾难。
所以我们实现了「选择性订阅」。
每个 connect 组件只订阅自己那一部分 state,一旦对应 slice 发生变化就触发 shouldComponentUpdate()。
用 shallowEqual 实现最小更新:
const shouldUpdate = !shallowEqual(prevProps, nextProps)
这个机制,让 Redux 拥有了性能上的杀手锏:局部响应式。
💡 connect 的架构演进(v6+)
在 v6+ 版本中,connect 的内部实现升级为 Context 分片隔离 + 函数组件兼容 + 性能进一步优化:
- 拆分了每个组件的订阅链;
- 引入
React.memo()和useContextSelector优化更新; - 更好支持
React.StrictMode和 Concurrent 模式; - 自动区分
mapStateToProps为factory function(更灵活)。
💡 为什么后来有了 useSelector 和 useDispatch?
我们设计 connect 的初衷是支持 class 组件,而函数组件崛起后,我们推出了新的 Hook API:
const value = useSelector(state => state.counter.value)
const dispatch = useDispatch()
这比 connect 更简洁、类型推导更清晰、逻辑更加聚合。
但背后其实还是一样的订阅模型。
🧪 小实验:你可以自己实现一个极简 connect
function connect(mapState, mapDispatch) {
return function (WrappedComponent) {
return function Connected() {
const store = useContext(StoreContext)
const [state, setState] = useState(() => mapState(store.getState()))
useEffect(() => {
const unsubscribe = store.subscribe(() => {
const newState = mapState(store.getState())
setState(newState)
})
return unsubscribe
}, [])
const dispatchProps = typeof mapDispatch === 'function'
? mapDispatch(store.dispatch)
: bindActionCreators(mapDispatch, store.dispatch)
return <WrappedComponent {...state} {...dispatchProps} />
}
}
}
⚖️ connect vs useSelector:我们如何选择?
| 场景 | 推荐方式 |
|---|---|
| 老项目、Class 组件 | connect |
| 新项目、函数组件为主 | useSelector |
| 大型团队模块化结构 | connect(清晰组织 map) |
| 想享受极致简洁 + 类型推导 | Redux Toolkit + Hooks |
🔚 总结:connect 是 React 与 Redux 的“翻译官”
Redux 是一个“后端思想”的状态容器,而 React 是“组件驱动”的视图系统。
connect 是这两者之间最重要的桥梁:
- 负责连接:store ↔️ 组件;
- 管理更新:局部订阅、最小渲染;
- 保持解耦:不污染组件逻辑,只注入 props。
在底层,它是性能优化的守门员;在架构上,它是前后端数据流的交汇点。
⏭️ 下一篇预告
我们将在下一篇《Redux 最佳实践:构建可维护、可扩展的状态架构》中,从团队协作视角出发,探讨:
如何组织 reducer? 如何模块化 store? 如何在大型项目中优雅使用 Redux Toolkit?