mirror of
https://github.com/penpot/penpot.git
synced 2026-08-07 21:38:48 +00:00
🐛 Fix leaked deferred DOM ops on dashboard navigation and template clone
The React reconciliation "removeChild" error surfaced during rapid dashboard navigation because several effects scheduled deferred DOM operations (focus, CSS positioning) without returning a cleanup that cancelled them. When the component unmounted before the callback fired, it ran against stale DOM and desynchronized React fiber tree from the actual DOM. - context_menu_a11y.cljs: replace tm/schedule-on-idle (30s idle window) with tm/schedule (setTimeout 0) and return a rx/dispose! cleanup. - dropdown.cljs: capture the tm/schedule handle and dispose it in the effect cleanup. - tooltip.cljs: capture the ts/raf handle and cancel it on cleanup. AI-assisted-by: opencode-go/mimo-v2.5-pro
This commit is contained in:
parent
85dbf14344
commit
b372e89282
@ -18,6 +18,7 @@
|
|||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.timers :as tm]
|
[app.util.timers :as tm]
|
||||||
|
[beicon.v2.core :as rx]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(def ^:private xf:options
|
(def ^:private xf:options
|
||||||
@ -229,8 +230,11 @@
|
|||||||
(partial ug/unlisten "penpot:context-menu:open" on-event)))
|
(partial ug/unlisten "penpot:context-menu:open" on-event)))
|
||||||
|
|
||||||
(mf/with-effect [ids]
|
(mf/with-effect [ids]
|
||||||
(tm/schedule-on-idle
|
(let [handle (tm/schedule
|
||||||
#(dom/focus! (dom/get-element (first ids)))))
|
(fn []
|
||||||
|
(some-> (dom/get-element (first ids))
|
||||||
|
(dom/focus!))))]
|
||||||
|
#(rx/dispose! handle)))
|
||||||
|
|
||||||
(when (some? levels)
|
(when (some? levels)
|
||||||
[:> dropdown-content* props
|
[:> dropdown-content* props
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
[app.util.globals :as globals]
|
[app.util.globals :as globals]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.timers :as tm]
|
[app.util.timers :as tm]
|
||||||
|
[beicon.v2.core :as rx]
|
||||||
[goog.events :as events]
|
[goog.events :as events]
|
||||||
[rumext.v2 :as mf])
|
[rumext.v2 :as mf])
|
||||||
(:import goog.events.EventType))
|
(:import goog.events.EventType))
|
||||||
@ -45,9 +46,10 @@
|
|||||||
(fn []
|
(fn []
|
||||||
(let [keys [(events/listen globals/document EventType.CLICK on-click)
|
(let [keys [(events/listen globals/document EventType.CLICK on-click)
|
||||||
(events/listen globals/document EventType.CONTEXTMENU on-click)
|
(events/listen globals/document EventType.CONTEXTMENU on-click)
|
||||||
(events/listen globals/document EventType.KEYUP on-keyup)]]
|
(events/listen globals/document EventType.KEYUP on-keyup)]
|
||||||
(tm/schedule #(mf/set-ref-val! listening-ref true))
|
timer (tm/schedule #(mf/set-ref-val! listening-ref true))]
|
||||||
#(run! events/unlistenByKey keys)))]
|
#(do (rx/dispose! timer)
|
||||||
|
(run! events/unlistenByKey keys))))]
|
||||||
|
|
||||||
(mf/use-effect on-mount)
|
(mf/use-effect on-mount)
|
||||||
children))
|
children))
|
||||||
|
|||||||
@ -322,25 +322,26 @@
|
|||||||
(let [trigger-el (mf/ref-val trigger-ref)
|
(let [trigger-el (mf/ref-val trigger-ref)
|
||||||
tooltip-el (mf/ref-val tooltip-ref)]
|
tooltip-el (mf/ref-val tooltip-ref)]
|
||||||
(when (and trigger-el tooltip-el)
|
(when (and trigger-el tooltip-el)
|
||||||
(ts/raf
|
(let [raf-id (ts/raf
|
||||||
(fn []
|
(fn []
|
||||||
(let [origin-brect (dom/get-bounding-rect trigger-el)
|
(let [origin-brect (dom/get-bounding-rect trigger-el)
|
||||||
tooltip-brect (dom/get-bounding-rect tooltip-el)
|
tooltip-brect (dom/get-bounding-rect tooltip-el)
|
||||||
window-size (dom/get-window-size)]
|
window-size (dom/get-window-size)]
|
||||||
(when-let [[new-placement placement-rect]
|
(when-let [[new-placement placement-rect]
|
||||||
(find-matching-placement
|
(find-matching-placement
|
||||||
placement
|
placement
|
||||||
tooltip-brect
|
tooltip-brect
|
||||||
origin-brect
|
origin-brect
|
||||||
window-size
|
window-size
|
||||||
offset)]
|
offset)]
|
||||||
(dom/set-css-property! tooltip-el "inset-block-start"
|
(dom/set-css-property! tooltip-el "inset-block-start"
|
||||||
(str (:top placement-rect) "px"))
|
(str (:top placement-rect) "px"))
|
||||||
(dom/set-css-property! tooltip-el "inset-inline-start"
|
(dom/set-css-property! tooltip-el "inset-inline-start"
|
||||||
(str (:left placement-rect) "px"))
|
(str (:left placement-rect) "px"))
|
||||||
|
|
||||||
(when (not= new-placement placement)
|
(when (not= new-placement placement)
|
||||||
(reset! placement* new-placement)))))))))))
|
(reset! placement* new-placement))))))]
|
||||||
|
#(ts/cancel-af! raf-id)))))))
|
||||||
|
|
||||||
[:> :div props
|
[:> :div props
|
||||||
children
|
children
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user