re-frame 中 `reg-event-fx` 返回 cofx 而非 fx 的陷阱:理解 effect 与 coeffect 的本质区别
前端Web框架【免费下载链接】re-frameA ClojureScript framework for building user interfaces, leveraging React项目地址https://gitcode.com/gh_mirrors/re/re-frame点击查看免费下载本文围绕 re-frame 的一个高频 FAQ 展开为什么在reg-event-fx注册的事件处理器中直接把修改后的第一个参数cofx图原样返回会触发no handler registered for effect: :event. Ignoring.警告读完本文你将彻底理解 coeffect世界状态输入与 effect副作用指令输出在 re-frame 架构中的定位差异掌握reg-event-fx的正确写法并能在源码与测试层面验证自己的理解。问题现象一段看起来没问题的代码假设你已经在用reg-event-db开发习惯性地把事件处理器当成一个reducing function传入db返回新的db。现在切换到reg-event-fx你很自然地写出了下面这段代码(reg-event-fx ::cow-clicked-bad (fn [cofx _] (update-in cofx [:db :clicks] inc)))它的意图是把第一个参数coeffects 图中:db键下的:clicks加 1然后把整个cofx返回出去。运行起来你会得到一个警告no handler registered for effect: :event. Ignoring.代码没有报错事件也确实处理了但警告一直在刷屏而且:clicks并没有真正增长。为什么核心结论Effects 根本不是 Coeffects这个 FAQ 给出的答案非常干脆Effects simply arent coeffects.reg-event-fx的事件处理器收到的是一个coeffect 值图当前世界的状态快照而它被要求返回的是一个effect 值图对世界的修改指令。两者是完全不同的东西只是恰好都以 map 的形式出现容易让人混淆。正确的写法是显式声明一个新的 effects map而不是把cofx修改后传回去(reg-event-fx ::cow-clicked (fn [{:keys [db] :as cofx} _] {:db (update db :clicks inc)}))这里{:db (update db :clicks inc)}是一张全新的、只包含 effect 指令的 map。reg-event-fx会把这返回值当作副作用描述处理:db这个 effect 键会触发把app-db重置为新的值。为什么会犯这个错reg-event-db的思维惯性如果你熟悉reg-event-db这种修改第一个参数并返回的写法是完全合理的(reg-event-db ::cow-clicked-db (fn [db _] (update db :clicks inc)))reg-event-db的处理器签名是(fn [db event] - db)它确实像一个 reducing functiondb进、db出。因为reg-event-db只暴露了db这一个 coeffect 给处理器返回的又是db所以就地修改再返回的模式是安全的。但reg-event-fx的处理器签名是(fn [cofx event] - effects)。它收到的cofx里装着世界状态它必须返回的却是对世界的修改指令。把这两者混为一谈就会写出上面那段触发警告的错误代码。深层原因:db的双重身份只是巧合看到{:db ...}既能出现在入参里又能出现在返回值中很多人会误以为cofx 和 fx 是同一张图的两次遍历。事实并非如此。re-frame 明确区分了两个概念coeffect事件处理器在计算时需要读取的世界状态比如:db、:event以及你自己通过reg-cofxinject-cofx注入的随机数、时间戳、LocalStore 数据等effect事件处理器在计算后希望世界发生的变化比如:db重置 app-db、:dispatch派发新事件、:dispatch-later延迟派发、:fx有序执行一组 effect等。reg-event-fx的处理器返回 map 后re-frame 会遍历这张 map 的每一个键去查找对应的effect handler通过reg-fx注册。如果某个键没有注册 effect handler就会打印警告并忽略它。:db 之所以能同时出现在 coeffect 图和 effect 图里是 re-frame 有意为之的设计决策——它恰好既是你要读的 app-db又是你要写回 app-db 的新值——但这只是命名上的巧合不是默认规律。警告从何而来cofx里还有:eventre-frame 在把cofx交给处理器之前除了:db还会自动注入其他 coeffect其中最典型的就是:event当前被处理的事件向量。于是那段错误代码实际上是在对一个类似这样的图做update-in{:db {:clicks 0} :event [::cow-clicked-bad] :your-other-coeffect 25}update-in会返回一整张图其中包含:event键。re-frame 在解释这张返回值时会去为:event查找 effect handler——但:event是一个 coeffect从来没有人、也不会有人为它注册 effect handler。于是就有了警告no handler registered for effect: :event. Ignoring.同样地任何你通过reg-cofx声明的自定义 coeffect比如上面例子里的:your-other-coeffect如果混进了返回值都会触发同样的警告——除非你恰好为同名的 key 注册过reg-fx。源码验证警告的确切来源这个警告并非凭空产生而是来自 src/re_frame/fx.cljc 中run-effects!的实现(doseq [[effect-key effect-value] effects-without-db] (if-let [effect-fn (effect-handler effect-key)] (effect-fn effect-value) (console :warn re-frame: no handler registered for effect: effect-key . Ignoring. (when ( :event effect-key) (str You may be trying to return a coeffect map from an event-fx handler. See https://day8.github.io/re-frame/FAQs/use-cofx-as-fx/)))))注意最后一段当缺失的 effect 键恰好是:event时re-frame 会额外提示你可能正在尝试从 event-fx handler 返回 coeffect map并直接指向本文所依据的 FAQ 文档。这是框架对这类常见错误的内置引导。对应的测试位于 test/re_frame/fx_test.cljs 的report-missing-handler用例中它注册一个返回{:fx-not-exist [:nothing :here]}的reg-event-fx处理器然后捕获日志并断言警告文本与re-frame: no handler registered for effect::fx-not-exist. Ignoring.匹配且只输出一条警告。这证明返回值中任何一个没有对应 effect handler 的键都会被忽略并告警与键名无关。机制拆解为什么返回值会被当作 effect 图要彻底理解这个问题需要看reg-event-fx背后完整的拦截器interceptor机制注册时自动注入拦截器。reg-event-fx最终调用-reg-event见 src/re_frame/core.cljc为每个事件在链首自动注入cofx/inject-db把app-db作为:db注入 coeffectsfx/do-fx负责在链条回程时执行 effects以及 flow、global interceptors 等。处理器包装。reg-event-fx使用fx-handler-interceptor包装你的处理器见 src/re_frame/std_interceptors.cljc其:before函数从 context 中取出:coeffects调用(handler-fn coeffects event)然后把返回值直接 assoc 进 context 的:effects(- (handler-fn coeffects event) (assoc context :effects))也就是说你返回的 map 会被原封不动地当作 effects map。如果你返回的是cofx那么cofx里的每一个键都会被当成 effect 键逐一处理——update-in后的:event、:your-other-coeffect就这样混进了 effects。do-fx在链条最后执行 effects。do-fx的:after函数取出:effects先保证:db最先执行再逐个调用各键对应的 effect handler见 src/re_frame/fx.cljc 中do-fx与run-effects!的实现。:dbeffect 的执行就是reset!app-db见同一文件中reg-fx :db的内置实现而:event等键则因找不到 handler 而落入警告分支。从源码结构看cofx 是给处理器读的、返回的 map 是给框架执行的这个边界由fx-handler-interceptor与do-fx的职责划分强制执行任何绕过它的写法如直接返回 cofx都会在运行时被警告拦截。effect 与 coeffect 速查对照维度coeffect入参effect返回值含义处理器计算所需的世界状态处理器希望发生的世界变化产生方式由reg-cofx注册、inject-cofx拦截器注入由reg-fx注册、do-fx拦截器执行典型键:db、:event、:now、:random-int、:local-store:db、:dispatch、:dispatch-n、:dispatch-later、:fx以:db为例app-db的当前值见 src/re_frame/cofx.cljc 内置:dbhandler新的 app-db 值do-fx会对其reset!见 src/re_frame/fx.cljc 内置:dbhandler返回值期望无处理器不返回 coeffect一张 effect 指令 map缺失 handler 的后果inject-cofx会打印No cofx handler registered for见 src/re_frame/cofx.cljc打印no handler registered for effect: ... . Ignoring.并忽略实践建议如何避免再踩这个坑记住两条签名不要混淆reg-event-db(fn [db event] - db)返回的就是新 dbreg-event-fx(fn [cofx event] - effects-map)永远返回一张新构建的 effects map而不是改造后的cofx。善用解构。推荐像 FAQ 中的正确写法那样在参数里直接取出:db(fn [{:keys [db] :as cofx} _] {:db (update db :clicks inc)})这样你只会把「世界状态」用于读取而返回值明确地只包含 effect 指令。为自定义 coeffect 命名留个心眼。如果你用reg-cofx声明了类似:db这种与 effect 同名的 coeffect属于有意的设计选择比如你想让某个 coeffect 恰好与某个 effect 共享数据通道但这是例外而非默认。绝大多数情况下自定义 coeffect 应该使用与任何reg-fxeffect 键都不冲突的名字否则一旦混入返回值就会触发 no handler registered for effect 警告。利用警告信息自查。当看到no handler registered for effect: key时先问自己这个key是我想声明的 effect 吗如果是:event或某个自定义 coeffect 名说明你很可能把cofx当成返回值了。需要完整理解两个概念时可进一步阅读仓库中的两份专题文档docs/Coeffects.mdcoeffect 的注入机制、reg-cofx用法、测试桩替身与 docs/Effects.mdeffect map 的构成、reg-fx自定义 effect、顺序保证与 noop 测试技巧以及内置于 src/re_frame/fx.cljc 与 src/re_frame/cofx.cljc 的源码实现。小结reg-event-fx的处理器收到 coeffect 图必须返回一张全新的 effects 图直接修改cofx并返回会让:event等 coeffect 键混进 effects触发no handler registered for effect: :event. Ignoring.警告该警告由 src/re_frame/fx.cljc 的run-effects!打印并有 test/re_frame/fx_test.cljs 的测试用例锁定其行为:db同时作为 effect 与 coeffect 是刻意的设计巧合不应推广为cofx 与 fx 可混用的一般结论正确姿势从cofx中读取世界状态返回一张只包含effect 指令的新 map。赞分享前端Web框架【免费下载链接】re-frameA ClojureScript framework for building user interfaces, leveraging React项目地址https://gitcode.com/gh_mirrors/re/re-frame点击查看免费下载相关推荐re-frame 纯事件处理实战用 reg-event-fx 将副作用与共效应转化为数据re frame 纯事件处理实战用 reg event fx 将副作用与共效应转化为数据 本指南围绕 re frame 仓库中的 EffectfulHandl前端Web框架上一篇Playnite游戏库管理器完整指南一个界面统一管理 Steam、Epic 与模拟器游戏下一篇如何快速上手UTMiOS设备运行虚拟机的10个技巧创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Apache Beam 管道创建实战指南:从 Pipeline 对象到运行一整套数据流水线

Apache Beam 管道创建实战指南:从 Pipeline 对象到运行一整套数据流水线

批处理流处理大数据 【免费下载链接】beam Apache Beam is a unified programming model for Batch and Streaming data processing. 项目地址: https://gitcode.com/gh_mirrors/beam15/beam 点击查看 免费下载 本文是 Apache Beam 官方文档《Create Your Pipeline…

2026/10/12 1:43:57 阅读更多 →
vLLM企业私有化部署实战指南:原理、量化与并发优化

vLLM企业私有化部署实战指南:原理、量化与并发优化

1. 为什么企业私有化部署首选vLLM先说结论:vLLM目前是企业私有化大模型部署里综合性价比最高的推理引擎,没有之一。这不是我一个人的判断,而是过去一年里我在多个项目里折腾过TGI、TensorRT-LLM、Text Generation Inference、SGLang之后得出的…

2026/10/12 1:43:57 阅读更多 →
魔改Informer实现滚动长期预测:误差修正与可视化实战

魔改Informer实现滚动长期预测:误差修正与可视化实战

简介:这份资源面向时间序列预测方向的研究者与研究生,尤其是准备发表论文、需要长期滚动预测实验的开发者。它基于Informer官方代码进行个人魔改,新增了自动滚动长期预测功能:首次预测未来24个时间点后,自动将预测值填…

2026/10/12 1:43:57 阅读更多 →

最新新闻

【深度学习新浪潮】Meta Muse 智能体:它是什么?有哪些特点?为什么突然火了?

【深度学习新浪潮】Meta Muse 智能体:它是什么?有哪些特点?为什么突然火了?

1. 引言 近期,Meta Muse 智能体在 AI 领域引发广泛关注,开发者、创作者与科技从业者纷纷展开讨论。许多初次接触者不禁疑惑:这是 Meta 推出的又一款大模型?抑或仅是蹭热度的 AI 玩具? 事实并非如此。Meta Muse 是 Meta 在 AI 智能体方向的一次战略性布局,它并非简单的对…

2026/10/12 2:24:22 阅读更多 →
Spring-boot-3 -注解 yaml配置 -日志

Spring-boot-3 -注解 yaml配置 -日志

4、核心技能1. 常用注解SpringBoot 摒弃 XML 配置方式,改为全注解驱动1. 组件注册Configuration 自定义配置类、SpringBootConfiguration 用来标注SpringBoot主启动类的Bean 可以在自定义配置类面创建对象交给ioc容器,组件在容器中的名字为方法名、Scope…

2026/10/12 2:24:22 阅读更多 →
Neuroimage: 动态功能连接方法的重测信度比较

Neuroimage: 动态功能连接方法的重测信度比较

本篇文献发表在Neuroimage杂志。所发布内容旨在与大家分享学术新知,促进交流学习版权归原作者或原出处所有,感谢各位学者的辛勤付出与研究成果。1.引言大脑的功能组织具有丰富的时空结构,可以使用功能连接指标进行探测。功能连接被定义为两个…

2026/10/12 2:24:22 阅读更多 →
page_alloc __rmqueue

page_alloc __rmqueue

__rmqueue() 是伙伴系统分配路径的核心调度器。它在持有 zone->lock 的前提下,按照碎片化风险从低到高的顺序,依次尝试不同的分配策略,直到成功或彻底失败。核心作用与策略链它的本质是一个多级降级策略链:先尝试最“干净”的方…

2026/10/12 2:24:22 阅读更多 →
游戏引擎中物理步进与动画采样的同步机制解析

游戏引擎中物理步进与动画采样的同步机制解析

1. 这不是教科书,是我在三个项目里拆过七次引擎后写下的物理与动画系统手记“游戏引擎架构深度解析(三):物理与动画系统”——看到这个标题,你大概率正卡在某个角色落地时穿模、布料抖动像癫痫发作、或者刚加完一个新关…

2026/10/12 2:24:22 阅读更多 →
产业与汇率全景分析深入分析多表格形成一篇文章

产业与汇率全景分析深入分析多表格形成一篇文章

产业与汇率全景深度分析:汇率是外生变量,产业是底层根基引言汇率从来不是孤立的数字,它是一国产业竞争力、贸易结构、资本流动、宏观政策、全球供需格局共同定价的结果;反过来,汇率波动又会重塑产业成本、订单、利润、…

2026/10/12 2:23:21 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/11 14:36:54 阅读更多 →