This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-08-02
Channels
- # announcements (11)
- # aws (3)
- # babashka (34)
- # beginners (20)
- # biff (2)
- # calva (3)
- # cherry (29)
- # cider (6)
- # cljs-dev (9)
- # clojure (124)
- # clojure-europe (12)
- # clojure-norway (5)
- # clojure-uk (2)
- # clojurescript (32)
- # conjure (11)
- # datalevin (1)
- # datomic (16)
- # deps-new (1)
- # etaoin (6)
- # holy-lambda (10)
- # honeysql (28)
- # hyperfiddle (21)
- # jackdaw (2)
- # jobs (2)
- # leiningen (15)
- # missionary (12)
- # off-topic (132)
- # other-languages (1)
- # pathom (13)
- # rdf (10)
- # re-frame (8)
- # reagent (5)
- # releases (1)
- # remote-jobs (4)
- # shadow-cljs (32)
- # tools-deps (6)
- # vim (15)
- # xtdb (24)
While profiling my app I noticed a bunch of detached html elements which cannot be garbage collected. Their numbers are in the thousands so it's a bit concerning. Some of them are pretty simple, don't have any listeners attached, just anchor tags whose urls are constructed dynamically. I tried putting those anchor elements in their own functions and passing in urls as arguments but that didn't help either. Any ideas what to try next? I'm using reagent with re-frame.
Not enough details.
Why exactly are those DOM tree elements not garbage collected? Something must be holding a reference, I wouldn't expect just there being <a>
tags to be the cause for what you see.
routes.cljs:
------------
(defmulti panels identity)
(defmethod panels :default [] [:div "No panel found for this route."])
(def routes
(atom
["/"
{"/posts" :posts
"/user" {["/" :id] :user}}]))
(defn dispatch
[route]
(let [panel (keyword (str (name (:handler route)) "-panel"))]
(re-frame/dispatch [::events/set-active-panel panel])))
(defonce history
(pushy/pushy dispatch parse))
(defn navigate!
[handler]
(pushy/set-token! history (url-for handler)))
views.cljs:
-----------
(defmethod routes/panels :posts-panel [] [posts-views/posts-panel])
(defmethod routes/panels :user-panel [] [posts-views/user-panel])
(defn main-panel []
(let [active-panel (re-frame/subscribe [::subs/active-panel])]
(routes/panels @active-panel)))
posts/views.cljs
----------------
(defn posts-list []
[:<>
(let [data @(re-frame/subscribe [::posts-subs/data])]
(if (empty? data)
[:div "No posts here"]
(for [[index d] (map-indexed vector data)]
[post d index]))])
(defn posts-panel []
(let [query-params (re-frame/subscribe [::subs/query-params])]
(re-frame/dispatch [::posts-events/http-get-post-list @query-params])
[:<>
[posts-list]]))
I've tried to narrow the issue down, it seems to me that detached elements start appearing when switching routes. For example, I display a list of posts on one route (posts-panel) and everything is fine, no detached elements. But then I go to user route (user-panel, which displays info about user plus the list of posts user posted which are displayed reusing the same view components from the main posts list) and the detached elements start appearing (all detached elements, including the anchor tags i mentioned, come from the post list).
I've simplified the code but hopefully it's enough to illustrate what I'm trying to do. This was my first time setting up an application with different routes in CLJS and also I don't have a very good understanding of defmulti / defmethod which are used to setup user and post panels (If I remember correctly, I got the idea for that code from here: https://pablofernandez.tech/2015/08/26/no-hashes-bidirectional-routing-in-re-frame-with-bidi-and-pushy/)Hold on. Previously, you mentioned "DOM elements not being garbage collected". Now you tell that those elements do actually exists in the DOM itself and are visible. "Not garbage collected" in this case means only that they're present in the memory. The fact that they are still rendered is the exact reason for why they're not garbage collected, so you should focus on why the elements are still rendered. Unfortunately, your code is still not nearly enough. But seems like finding the issue will require quite a bit of code so what I can suggest is to spend some time and create a so-called minimal reproducible example. One that contains everything that's needed to reproduce the issue, but not more - it's much harder to dig through a full-on app rather than through an isolated reproduction case. Once you do that, share a public Git repo and I'll try to reproduce it.
I think I managed to get rid off this issue. I stripped it down to minimally reproducible example, like you said, and and then worked my way back. In the end it didn't have anything to do with Reagent nor Cljs. It came down to mostly two things: conflicts with legacy javascript (which I had to include into the page along with my Cljs app) and React version. Once I upgraded to React 18, along with getting rid of some components from legacy code, the number of detached elements became reasonable. What I mean by reasonable is that it didn't double every time I switched between two views components back and forth. They weren't rendered on the page anymore but they weren't being garbage collected either so their numbers were growing more and more.