This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-06-19
Channels
- # announcements (2)
- # beginners (448)
- # calva (10)
- # cider (27)
- # clojure (121)
- # clojure-argentina (2)
- # clojure-brasil (6)
- # clojure-chicago (1)
- # clojure-colombia (9)
- # clojure-ecuador (1)
- # clojure-europe (1)
- # clojure-italy (14)
- # clojure-mexico (1)
- # clojure-nl (19)
- # clojure-spec (11)
- # clojure-uk (160)
- # clojurescript (25)
- # core-async (7)
- # cursive (8)
- # data-science (1)
- # datascript (1)
- # datomic (4)
- # devops (2)
- # graalvm (3)
- # hoplon (1)
- # immutant (1)
- # jackdaw (32)
- # jobs (2)
- # joker (2)
- # keechma (9)
- # leiningen (26)
- # luminus (13)
- # off-topic (2)
- # overtone (2)
- # quil (5)
- # reagent (38)
- # reitit (13)
- # shadow-cljs (16)
- # spacemacs (2)
- # tools-deps (4)
- # xtdb (12)
Hello all. We have a reagent template page that shows at toolbar, content and optionally a popover dialog.
(defn page []
[:div
(when @show-popover
[popover ...])
[toolbar ...]
[content ...]])
When we (reset! show-popover true)
to show the dialog we’ve found that the VDOM for toolbar
and content
get fully re-built even though they’re not dependent on it. If we move the (when @show-popover ...)
last, they don’t.
This is quite surprising, and I’m wondering if it’s accidental or if I’m missing something important about reagent/react?This shows that the re-renders are targeted even at the hiccup level (pre-VDOM):
(def state (atom nil))
(def output (atom []))
(defn subcomponent [prop]
(swap! output conj prop)
[:span prop])
(defn root []
(swap! output conj "root")
[:div
[:button {:on-click #(swap! state not)} "toggle"]
(when @state
[subcomponent "toggle"])
[subcomponent "fixed"]])
(defn view []
(swap! output conj "view")
[:div
[:div [:p (prn-str @state)]]
[:div [:p (prn-str @output)]]])
[:div
[root]
[:hr]
[view]]
(To be pasted on https://escherize.com/cljsfiddle/)If you're on mobile, output
contains:
• ["root" "fixed" "view"]
on mount
• ["root" "fixed" "view" "root" "toggle" "view"]
after one click
• ["root" "fixed" "view" "root" "toggle" "view" "root" "view"]
after two clicks
Yes, actually I probably misspoke when I said VDOM. This is at the reagent hiccup->VDOM level
It seems that perhaps reagent re-renders all the components that rendered after a deref
this doesn’t necessarily mean that it wil be committed to the DOM, but it has to build the tree in order to diff it
I know that in React, you can actually short-circuit this by returning the exact same React elements as the last render. I think I remember it works the same in Reagent (by returning the same hiccup vector), but I can’t be sure
if that’s the case with reagent, this is still tricky because our renders can have side effects (dereferences) so we can’t just blindly memoize our components
but the interesting thing here is all the children aren’t being re-rendered in the case where the affected one is last (these are siblings)
If you extract the when
to a separate component, it will not trigger re-rendering of the siblings inside page
, but I couldn't mimic the behavior you described.
I have the following here:
(defn page []
(.log js/console "page")
[:div
[:div "toolbar"]
[:div "content"]
(when @show-popover
(.log js/console "popover in")
[:div "popover"])])
And presuming show-popover
is false
and I (reset! show-popover true)
, both page
and popover in
are printed out to the console, independent of the position of the when
statement. I believe this is the intended behavior as it would be pretty weird otherwise. How are you testing the re-rendering?
(defn popover []
(when @show-popover
(.log js/console "show-popover outside")
[:div "popover"]))
(defn page []
(.log js/console "page")
[:div
[popover]
[:div "toolbar"]
[:div "content"]])
we could see the performance hit, and added printlns
into the components on the page. tried a bunch of things and stumbled on changing the order
so very sure we saw what I’m describing. will look at the actual code again to make sure my example is accurate
Now I'm curious as well 🙂
> It seems that perhaps reagent re-renders all the components that rendered after a deref
I'm not sure I understand this statement.
At least on the example I've posted, fixed
is only rendered once even though both the parent and sibling have re-rendered.
Just tested that this also happens regardless of its position!
@U1UQEM078 I’m not sure what’s going on here then. The actual code we’re using is of course more complex, but the updated example I posted captures pretty much all of its structure, and the order definitely makes a difference
I think I’ve probably had my question answered: the way we were doing it should have caused a re-render of all regardless of order. the fact that order matters may be some kind of bug or implementation detail/optimisation
If you end up figuring out what happened please share.
yep, will do
If you extract the when
to a separate component, it will not trigger re-rendering of the siblings inside page
, but I couldn't mimic the behavior you described.
I have the following here:
(defn page []
(.log js/console "page")
[:div
[:div "toolbar"]
[:div "content"]
(when @show-popover
(.log js/console "popover in")
[:div "popover"])])
And presuming show-popover
is false
and I (reset! show-popover true)
, both page
and popover in
are printed out to the console, independent of the position of the when
statement. I believe this is the intended behavior as it would be pretty weird otherwise. How are you testing the re-rendering?
(defn popover []
(when @show-popover
(.log js/console "show-popover outside")
[:div "popover"]))
(defn page []
(.log js/console "page")
[:div
[popover]
[:div "toolbar"]
[:div "content"]])
we could see the performance hit, and added printlns
into the components on the page. tried a bunch of things and stumbled on changing the order
@anantpaatra I don’t think we tried moving the when
to a sibling component
(trying it now)
Dereferencing will trigger a re-render of the entire component. Normally the impact in performance isn't big (in my little experience), but that's exactly the case for moving something to a subcomponent.
In the case of a little component, that is. if it is something more complex the impact in performance will be visible of course.
moving the deref to another component fixes it, which makes total sense. don’t know how we missed that
but def seeing the effect of ordering still
I don’t know if it makes any difference, but our example is more like:
(defn page []
(let [v @some-other-ratom]
[:div
[toolbar v ...]
[content v ...]
(when @show-popover
[popover])]))
v
in this case isn’t being touched, but is deref’ed
it’s sounding like it would be worth taking this to a github issue
Hmm, just tried that example and I get the same result. The ordering doesn't change what happens. 😕 In my test @some-other-ratom
is just a simple map that I prn-ln
in the toolbar
and content
component calls though, so maybe there is something else going on.
@anantpaatra weird. thanks for testing it out for me :thumbsup:
No problem 🙂 All's well that ends well. haha
What is difference between r/as-component
and r/reactify-component
?
if a component takes a component you would use reactify-component
if it takes dom nodes you would use as-component