前端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),仅供参考