如何抑制re-frame重渲染?避免中间状态触发不必要重渲染
当然可以搞定!针对你遇到的这种「连续触发[:a]和[:b]事件后,数据库先变到db-1再回到初始db-0,却触发了两次重渲染」的问题,有两个非常实用的解决办法:
方案1:用re-frame.core/batch批量处理事件
re-frame原生提供了batch函数,专门用来解决这类批量事件的重渲染优化问题。它会把包裹在其中的所有dispatch调用合并处理,只有当所有事件都执行完毕、数据库状态稳定后,才会统一触发一次重渲染,完全跳过中间状态的渲染步骤。
具体用法很简单,把你的两次dispatch放进batch里就行:
(re-frame.core/batch (dispatch [:a]) (dispatch [:b]))
这样不管[:a]和[:b]内部怎么修改数据库,只有当两个事件都执行完、db回到初始状态后,re-frame才会检查订阅的变化——如果最终状态和初始状态完全一致,甚至可能不会触发任何重渲染。
方案2:合并事件处理器(逻辑允许的情况下)
如果[:a]和[:b]这两个事件本来就是强关联、需要连续执行的逻辑,那更干脆的做法是把它们的处理逻辑合并到同一个事件处理器里。这样数据库只会被修改一次(从db-0直接计算到db-0,没有db-1的过渡状态),自然就不会有中间重渲染的问题。
举个例子,原来的两个独立处理器:
(reg-event-db :a (fn [db _] ;; 把db从db-0改成db-1的逻辑 updated-db-1)) (reg-event-db :b (fn [db _] ;; 把db从db-1改回db-0的逻辑 original-db))
可以合并成一个新的事件处理器:
(reg-event-db :a-then-b (fn [db _] ;; 依次执行:a和:b的逻辑,直接返回最终的db-0 (-> db (process-a) (process-b))))
之后只需要触发这个合并后的事件(dispatch [:a-then-b])即可,整个过程数据库只会更新一次,完全避免了中间状态的渲染开销。
额外提示
batch的适用场景不止这一种——任何时候你需要连续触发多个事件、想减少重渲染次数时,都可以用它来优化性能。它的核心原理是暂时关闭re-frame的渲染触发机制,等批量内的所有事件处理完成后,再一次性检查订阅的变化并触发渲染。
内容的提问来源于stack exchange,提问作者deadghost

