Redux `compose(...functions)` 完全指南:从右向左的函数组合与 Store Enhancer 应用

发布时间:2026/11/12 16:31:10

Redux `compose(...functions)` 完全指南:从右向左的函数组合与 Store Enhancer 应用 Reduxcompose(...functions)完全指南从右向左的函数组合与 Store Enhancer 应用【免费下载链接】reduxA JS library for predictable global state management项目地址: https://gitcode.com/gh_mirrors/re/reduxcompose是 Redux 核心包自带的一个函数式编程工具用于从右向左组合多个函数将f(g(h(...args)))这类深层嵌套调用展平为compose(f, g, h)(...args)。在 Redux 中最典型的应用场景是当使用createStore时需要一次性传入多个 store enhancer如applyMiddleware与 DevTools 的instrument()时用compose将它们合并为单一 enhancer。本文基于 Redux 仓库中的 compose API 文档结合 compose 源码实现、单元测试 与 TypeScript 类型测试完整讲解其参数约定、返回值、边界行为与底层原理。概述什么是compose(...functions)compose把多个单参数函数按从右到左的顺序组合成一个函数最右侧的函数最先执行其返回值依次作为左侧函数的入参。它本身不引入任何新逻辑只是把函数嵌套调用改写为管道式的调用写法从而避免代码向右无限缩进rightward drift。在 Redux 中它的主要用途是连续应用多个 store enhancer。一个 store enhancer 是高阶函数签名形如createStore createStore更完整的类型定义见 src/types/store.ts用于以可组合的方式增强 store 的能力例如增加中间件、实现时间旅行调试等。createStore只接受一个enhancer 参数因此当你有多个 enhancer 时就必须用compose把它们合并。import { createStore, applyMiddleware, compose } from redux import { thunk } from redux-thunk import DevTools from ./containers/DevTools import reducer from ../reducers const store createStore( reducer, compose(applyMiddleware(thunk), DevTools.instrument()) )此外compose也是一个完全独立的通用工具函数可以脱离 Redux 单独使用例如组合数据处理管道、装饰器等。:::warning 注意在日常开发中你不必手动调用compose。Redux Toolkit 的configureStore方法会自动配置好标准的applyMiddleware与 Redux DevTools store enhancer并通过enhancers参数接收额外 enhancer。本文所述的手动组合方式主要用于理解 Redux 核心原理或维护仍基于createStore的遗留代码库。:::参数Argumentscompose(...functions)接受任意数量的函数作为参数规则如下每个函数预期接收单个参数。函数的返回值会作为其左侧函数的入参依次向左传递。唯一的例外是最右侧的函数它可以接收多个参数因为它为最终组合出来的函数提供了完整签名——最终调用compose(f, g, h)(a, b, c)时参数(a, b, c)会先全部传给最右侧的h再由g接收h的返回值最后由f接收g的返回值。这一约定在源码注释中有明确表述见 src/compose.tscompose(f, g, h)等价于(...args) f(g(h(...args)))。返回值ReturnsFunction将给定函数从右向左组合后得到的最终函数。组合结果函数的调用签名由最右侧函数的签名决定。实现原理源码逐层拆解compose的运行时实现非常精简完整逻辑位于 src/compose.tsexport default function compose(...funcs: Function[]) { if (funcs.length 0) { // 空参数返回恒等函数 return T(arg: T) arg } if (funcs.length 1) { // 单参数原样返回该函数 return funcs[0] } return funcs.reduce( (a, b) (...args: any) a(b(...args)) ) }三个分支的边界行为零个函数返回恒等函数arg arg即compose()(value)原样返回value。源码注释说明这是为了在后续类型推断中保留下行参数类型。一个函数直接返回该函数本身不做任何包装。测试 test/compose.spec.ts 验证了compose(fn)与fn是同一引用。多个函数使用Array.prototype.reduce从右向左折叠。关键在于 reduce 的折叠方向[f, g, h].reduce((a, b) (...args) a(b(...args)))的执行过程是——第一步得到(...args) f(g(...args))第二步再与h组合得到(...args) f(g(h(...args)))。最终结果是h最先执行这与从右向左组合的语义完全一致。类型层面的重载设计为了在编译期精确推导组合结果的类型compose在 src/compose.ts 中声明了多组函数重载逐一覆盖从 0 到 4 个函数的组合以及 rest 参数的兜底签名compose(): R(a: R) R零参数时的恒等函数composeF extends Function(f: F): F单参数时保持原类型两个至四个函数泛型按A → B → C → R的链条逐级约束例如三函数签名f1: (b: B) R, f2: (a: A) B, f3: FuncT, Arest 兜底f1: (a: any) R, ...funcs: Function[]。类型测试 test/typescript/compose.test-d.ts 覆盖了这些场景例如compose(numberToString, numberToNumber)(5)被推导为stringcompose(stringToNumber, numberToString, numberToNumber)(5)被推导为number多参数函数放在最右侧时如multiArgFn组合结果仍可接收(bar, 42, true)三个实参。实战用法一组合 store enhancer这是compose在 Redux 中最核心的用途。createStore的签名支持createStore(reducer, preloadedState?, enhancer?)见 src/createStore.ts当检测到 enhancer 时会以enhancer(createStore)(reducer, preloadedState)的方式交给 enhancer 处理src/createStore.ts。注意createStore只接受一个 enhancer如果直接传入多个 enhancer 会抛出运行时错误It looks like you are passing several store enhancers to createStore(). This is not supported. Instead, compose them together to a single function.见 src/createStore.ts——这正是compose存在的意义。官方文档示例 docs/api/compose.md 展示了标准组合方式import { createStore, applyMiddleware, compose } from redux import { thunk } from redux-thunk import DevTools from ./containers/DevTools import reducer from ../reducers const store createStore( reducer, compose(applyMiddleware(thunk), DevTools.instrument()) )这里compose把两个 enhancer 合成一个DevTools.instrument()包裹applyMiddleware(thunk)的结果最终返回一个统一的 enhancer 传给createStore。顺序很重要中间件必须放在前面applyMiddleware的文档docs/api/applyMiddleware.md明确建议如果除了applyMiddleware还有其他 enhancer务必把applyMiddleware放在组合链的前面即更靠右的位置先执行因为中间件可能是异步的。若将 DevTools 放在applyMiddleware之前DevTools 将看不到 Promise 等异步中间件产生的原始 action。条件性应用中间件的写法当你需要按环境条件加载中间件时可以结合展开运算符与composelet middleware [a, b] if (process.env.NODE_ENV ! production) { const c require(some-debug-middleware) const d require(another-debug-middleware) middleware [...middleware, c, d] } const store createStore( reducer, preloadedState, applyMiddleware(...middleware) )这种方式便于打包工具如 webpack在 production 构建中剔除未使用的模块减小产物体积。实战用法二中间件链的底层组装compose不仅在用户代码中出现也是 Redux 内部实现的核心。看 applyMiddleware 的源码export default function applyMiddleware( ...middlewares: Middleware[] ): StoreEnhancerany { return createStore (reducer, preloadedState) { const store createStore(reducer, preloadedState) // ... const chain middlewares.map(middleware middleware(middlewareAPI)) dispatch composetypeof dispatch(...chain)(store.dispatch) // ... } }中间件形如({ getState, dispatch }) next action见 docs/api/applyMiddleware.md 的参数说明调用后生成next action形式的函数链。applyMiddleware用compose(...chain)把这串函数从右向左组合把最内层的store.dispatch作为最右侧的next传入——这正是 test/compose.spec.ts 中composes functions from right to left测试所验证的模式compose(a, b, c)(final)()的结果为abc证明final最先被调用、a最后执行即后列出的中间件更靠近真实 dispatch先列出的中间件最先处理 action。因此compose本质上承担着中间件洋葱模型的装配职责action 从最左侧中间件进入逐层向里传递最终到达 store 的原始dispatch返回值再沿原路径向外返回。实战用法三通用函数组合作为通用工具compose可以直接组合数据处理函数例如import { compose } from redux const double x x * 2 const square x x * x // 等价于 square(double(5)) 100 compose(square, double)(5) // 100 // 等价于 double(square(double(5))) 200 compose(double, square, double)(5) // 200测试 test/compose.spec.ts 覆盖了这类场景。需要注意的是运行时对参数类型没有强校验若某个位置传入非函数会在调用组合结果时抛出TypeError测试 test/compose.spec.ts 验证了传入false、undefined、true、NaN、42等值会抛错但在 TypeScript 中这些错误用法会在编译期被类型系统拦截。多参数种子最右侧函数接收全部实参当最右侧函数接收多个参数时组合结果的签名随之扩展。例如测试 test/compose.spec.tsconst square (x, _) x * x const add (x, y) x y compose(square, add)(1, 2) // (1 2) ** 2 9这里add(1, 2)接收全部实参square接收其返回值最终结果是 9。这个最右侧函数决定组合签名的特性与中间件链中store.dispatch作为最右侧接收单个 action 的场景形成对照。常见错误与排查给createStore传多个 enhancer直接写createStore(reducer, enhancerA, enhancerB)会触发 src/createStore.ts 中的运行时错误正确做法是用compose(enhancerA, enhancerB)合并成一个。把多参数函数放在非最右侧位置compose只保证最右侧函数可接收多个参数中间的每个函数都只接收其右侧函数的单个返回值。若中间函数本身需要多参数属于用法错误TypeScript 重载会在编译期报错。空参数调用compose()返回恒等函数compose()(3)仍得到 3、compose()(undefined)仍得到undefined见 test/compose.spec.ts可用于需要可选的组合的场景。Tips正确看待compose正如 官方文档 所强调的compose的全部作用只是让你不用写出层层嵌套的函数变换从而避免代码向右漂移。它没有引入任何新的运行时语义——compose(f, g, h)(x)与f(g(h(x)))完全等价。因此不必对它抱有过高期待把它当作一个语法层面的便利工具即可。延伸阅读在 store enhancer 语境下理解compose的用途可阅读 术语表store enhancer 以及 Store API 文档想了解compose组合的对象——applyMiddleware可阅读 applyMiddleware 文档想了解 enhancer 的入口——createStore的参数与校验逻辑可阅读 createStore 文档Redux 顶层导出清单见 API Referencecompose与其他核心 APIcreateStore、combineReducers、applyMiddleware、bindActionCreators一同从包入口 src/index.ts 导出源码与测试参考compose 实现、compose 单元测试、compose 类型测试、applyMiddleware 中的 compose 调用。【免费下载链接】reduxA JS library for predictable global state management项目地址: https://gitcode.com/gh_mirrors/re/redux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/11/12 16:31:10

论文降AI率工具对比与学术写作优化实践

1. 论文降AI率工具的市场需求去年帮导师审研究生论文时发现一个现象:超过60%的初稿都存在明显的AI写作痕迹。最典型的特征是段落结构过于工整、术语堆砌但缺乏逻辑衔接、参考文献格式整齐得不像人工整理。这让我意识到,随着AI写作工具的普及,…

2026/11/12 16:31:09

完整重复文件清理神器 Czkawka:给硬盘瘦身

完整重复文件清理神器 Czkawka:给硬盘瘦身 【免费下载链接】czkawka Multi functional app to find duplicates, empty folders, similar images etc. 项目地址: https://gitcode.com/GitHub_Trending/cz/czkawka 年度手机备份之后,你发现照片库里…

2026/11/11 8:45:05

Linux入门指南:从虚拟机搭建到终端命令实战

1. Linux系统初探:从零开始的数字世界漫步第一次接触Linux时,我被终端里闪烁的光标和神秘的命令行震撼了。这个诞生于1991年的开源操作系统,如今已渗透到我们数字生活的每个角落——从智能手机到超级计算机,从智能家电到金融交易系…

2026/11/12 16:26:51

成都安监局考电工证怎么转异地 证书转过来多少钱你知道吗

成都安监局考电工证怎么转异地 证书转过来多少钱你知道吗 之前考的证在外省能不能转过来,这是很多电工朋友在考虑换城市、换工作时最关心的问题。如果你是外地考的电工证,现在想在成都发展,或者已经落地成都,但证书还在老家,那这篇内容就是为你准备的。…

2026/11/12 16:26:51

天津去哪里办电工证怎么选培训机构 上岗必备的全流程解析

天津去哪里办电工证怎么选培训机构 上岗必备的全流程解析 证书过期了不知道怎么复审延期?别慌,先搞清楚天津去哪里办电工证,再选对培训机构,上岗必备的电工证才能稳稳拿到手。这篇文章专为像你一样的工程类从业者写,从证书过期怎么办,到培训选哪个机构…

2026/11/12 16:26:51

电工证一日取证多久 电子证书怎么查

电工证一日取证多久 电子证书怎么查 之前考的证在外省能不能转过来?这个问题,不少在外地工作的电工朋友都问过。特别是像济源这类地区的转岗从业者,可能手头有证却因为跨省、跨行业,导致证书用不上、查不到,甚至被误认为无效。其实,随着国家对特种作业…

2026/11/12 16:26:51

焦作如何考电工证?郑州报考避坑指南帮你避免白交培训费

焦作如何考电工证?郑州报考避坑指南帮你避免白交培训费 怕考不过白交培训费?我懂你,去年在周口准备考低压电工证的时候,我也遇到过培训机构“保证包过”的套路,结果考试一塌糊涂,钱花了不说,还浪费了大半年时间。别急,这篇【焦作如何考电工证】的详细…

2026/11/12 16:21:51

乐山井研哪里考电工证全国通用?避坑指南来了

乐山井研哪里考电工证全国通用?避坑指南来了 不知道去哪报名怕被中介坑?乐山井研哪里考电工证,怎么避免被坑,又怎么顺利拿到全国通用的电工证?不少想考电工证的朋友都在问,但真正能给出靠谱答案的却不多。今天就从一个从业多年的视角,带你看清整个流程…

2026/11/11 12:42:59

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/12 0:04:01

工地太忙根本没时间复习考试?学习电工证到哪里学通过率揭秘

工地太忙根本没时间复习考试?学习电工证到哪里学通过率揭秘 工地太忙根本没时间复习考试?你不是一个人。作为一名在南阳卧龙区干了七八年市政公用工程的电工,我深知这个问题的现实性。电工证不仅是工作的“入场券”,更是职业发展的“通行证”,但备考过程…

2026/11/12 0:04:01

双流电工证考别怕挂科!郑州报考避坑指南全攻略

双流电工证考别怕挂科!郑州报考避坑指南全攻略 实操考试心里没底怕挂科?双流电工证考在郑州报名,很多人因为不了解流程、准备不充分,白白浪费时间和金钱。作为三门峡应届工程类毕业生,我也曾经历过这样的焦虑。今天就从零开始,手把手教你如何避开郑州报…

2026/11/12 0:04:01

之前考的证在外省能不能转过来 怎么报名考电工证培训价格多少

之前考的证在外省能不能转过来 怎么报名考电工证培训价格多少 之前考的证在外省能不能转过来?这个问题确实让不少准备换城市工作的朋友犯了难,尤其像电工证这种实操性很强的证书,很多人担心跨省后证书能不能继续用。其实,只要符合相关流程,是可以顺利转…

2026/11/11 20:27:01

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/12 9:51:42

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑