Redux createStore搭配enhancer的执行逻辑与接口定义疑问
Redux 搭配 Enhancer 时 createStore 工作机制解析
已确认逻辑对齐
你目前理解的逻辑是准确的:sayHiOnDispatch 是标准的Redux store enhancer,采用柯里化结构,接收原生createStore作为入参,闭包持有内部的新dispatch逻辑,最终返回增强后的store对象。
5个核心疑问解答
调用方是Redux内部的sayHiOnDispatch的调用位置createStore主逻辑。当你将enhancer传入createStore时(无论是直接作为对应位置参数传,还是通过applyMiddleware组合后传入),Redux会先做参数校验,识别到传入了enhancer后,会将未被任何增强包装的原生createStore方法作为入参,执行sayHiOnDispatch。内部匿名函数的触发方式
先看给出的TS类型定义就能找到答案:
类型明确说明:enhancer执行后的返回值,是一个和原生createStore结构完全一致的export type StoreEnhancer<Ext = {}, StateExt = never> = ( next: StoreEnhancerStoreCreator<Ext, StateExt> ) => StoreEnhancerStoreCreator<Ext, StateExt>StoreEnhancerStoreCreator(也就是接收reducer、preloadedState返回store的函数)。Redux拿到这个返回的匿名函数后,会直接用它替换掉原生createStore的引用,再把你传入的rootReducer、preloadedState、剩余待执行的enhancers作为入参,调用这个匿名函数完成store初始化。
这个返回值就是你业务代码里写{ ...store, dispatch: newDispatch }的接收方const store = createStore(xxx)时,等号左边的store变量接收到的值。Redux会把内部匿名函数执行的返回值,直接作为createStore的最终返回值抛给上层调用方。
所有业务侧发起的newDispatch的调用方store.dispatch(action)调用,调用的都是这个newDispatch。因为最终返回给你的store对象,已经把原生dispatch替换成了newDispatch,你拿到的不是原生createStore生成的原始store,是被增强后覆盖了dispatch字段的版本,所有dispatch调用都会先进入newDispatch执行自定义逻辑,再在newDispatch内部调用闭包持有的原生store.dispatch完成原有分发逻辑。
对应示例代码的逻辑非常清晰:export const sayHiOnDispatch = (createStore) => { return (rootReducer, preloadedState, enhancers) => { // 这里拿到的是原生store const store = createStore(rootReducer, preloadedState, enhancers) function newDispatch(action) { // 内部调用原生dispatch保证原有功能正常 const result = store.dispatch(action) // 新增自定义逻辑 console.log('Hi!') return result } // 覆盖dispatch字段返回 return { ...store, dispatch: newDispatch } } }TS类型定义的解读方法
读这类嵌套函数类型,遵循「从外到内拆,只看单层级契约」的原则即可,不要一上来啃整段长类型:- 先拆最外层类型:
type A = (入参) => 返回值,先明确A本身是个函数,入参是什么类型,返回值是什么类型。比如StoreEnhancer最外层明确是「入参是store创建函数,返回值也是store创建函数」,本质就是个给store创建函数加包装的装饰器。 - 再拆嵌套的类型别名:比如
StoreEnhancerStoreCreator,单独拿出来看:
拆解后就能看到:这个函数接收reducer、可选的preloadedState,返回增强后的Store对象,和原生createStore的签名完全一致,这就对应了前面说的「enhancer返回的函数要替代原生createStore使用」的逻辑。export type StoreEnhancerStoreCreator<Ext = {}, StateExt = never> = < S = any, A extends Action = AnyAction >( reducer: Reducer<S, A>, preloadedState?: PreloadedState<S> ) => Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext - 遇到泛型不用慌,先把泛型占位符当成「会被实际入参类型替换的占位符」即可,先理清楚函数的入参返回结构,再看泛型是给哪个部分做类型扩展。
- 先拆最外层类型:
通用解读思路(可举一反三)
这类多层柯里化的装饰器/中间件代码,不用硬顺着执行顺序死磕,按三个步骤走就能快速捋清逻辑:
- 先抓契约:每一层函数先看入参和返回值的结构,如果某一层的返回值和入参结构一致,基本就是装饰逻辑——不改变原有方法的调用方式,只在前后加自定义逻辑。
- 找替换点:找到代码里把原生方法引用换成包装后方法的位置,比如Redux内部用enhancer返回的函数替换原生createStore,后续所有调用都会走增强逻辑,整条链路的核心节点就通了。
- 顺持有关系:想知道某个返回值/函数最终被谁调用,就顺着变量赋值链路往上找:比如newDispatch被挂到返回的store上,store被返回给业务侧的变量,业务侧调用store.dispatch自然就触发了newDispatch,不需要猜黑盒逻辑。
内容的提问来源于stack exchange,提问作者alex kriezelman
相关产品推荐
相关产品推荐

