This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-11-04
Channels
- # announcements (13)
- # beginners (51)
- # boot (3)
- # calva (10)
- # cider (20)
- # clj-kondo (55)
- # cljs-dev (60)
- # clojure (99)
- # clojure-europe (6)
- # clojure-gamedev (9)
- # clojure-italy (19)
- # clojure-nl (7)
- # clojure-spec (20)
- # clojure-uk (42)
- # clojurescript (96)
- # clojurex (37)
- # clojutre (1)
- # cursive (37)
- # data-science (2)
- # datomic (15)
- # defnpodcast (9)
- # duct (7)
- # emacs (6)
- # events (9)
- # fulcro (124)
- # jackdaw (4)
- # jobs (4)
- # leiningen (9)
- # malli (7)
- # mount (3)
- # off-topic (109)
- # other-languages (8)
- # re-frame (39)
- # reagent (4)
- # reitit (6)
- # remote-jobs (2)
- # rewrite-clj (36)
- # ring (4)
- # shadow-cljs (16)
- # spacemacs (16)
- # tools-deps (91)
- # vim (8)
- # yada (2)
so what is the best way to debug "render" problems? I have a bunch of messages streaming in from a websocket and often re-renders seem to miss. meaning that all the state is updated properly but the UI doesn't update. I can hit the render button in fulcro-inspect and it'll update the UI properly. as far as I can tell I'm telling the UI what to refresh via (fc/transact! app [] {:refresh vec-of-idents})
which I assumed would queue an actual render by maybe doesn't?
@thheller Re-render should be done with the functions in the application
ns, not as an empty tx. Also, you say you have messages streaming in…how are you integrating that state into the app itself?
then it is likely you’re not getting optimal refresh, but the answer remains the same: app ns refresh functions…schedule-render is recommended. You might switch to the keyframe renderer if you’re not using ident-based normalization
I added some logging and whenever I queue the refresh it does actually render afterwards
no need to take the analysis overhead if you’re just going to end up with a root render anyhow
if I trigger a full app refresh for every single websocket message the whole thing never catches up
so, you do have normalized data…it’s just your messages are hitting known things that have no nested state…I think I misunderstood earlier
oh yeah sorry. the data is normalized. I mostly just (swap! state update-in [::some id ::foo] merge some-data)
the default render does an optimized diff on state based on on-screen components’ idents
no, it somehow misses an update. If I press "re-render" in fulcro-inspect everything is fine
your problem is almost certainly that you are rendering something that is not represented in the component query
hmm is this maybe the problem?
:query
(fn []
[::rid
{::runtime [::runtime-info
::supported-ops]}
{::objects (fc/get-query ObjectListItem)}
{::object (fc/get-query ObjectDetail)}
::nav-stack])}
joining without a component?The conversion to AST would be missing a component (and ident)…but there is nothing that would mount there (supported ops is rendered in that component I assume)
In general if you have a non-normalized chunk of opaque data you don’t represent it in the query (since it will just be extra db->tree overhead)
:query
(fn []
[::rid
{::runtime (fc/get-query Runtime)}
{::objects (fc/get-query ObjectListItem)}
{::object (fc/get-query ObjectDetail)}
::nav-stack])}
(defsc Runtime [this {::keys [rid] :as props}]
{:ident
(fn []
[::rid rid])
:query
(fn []
[::rid
::runtime-info
::supported-ops])}
(html/div "runtime" (pr-str props)))
(def ui-runtime (fc/factory Runtime {:keyfn ::rid}))
I’m saying that the runtime stuff can just be an opaque map at the ::runtime key on that parent
OK, so the diff on the parent checks to see if the IDENT of the child changed. It is expecting the child itself to manage its own refresh…parent (when normalized) only cares if the identity of the child changed
If you make the child a “scalar” (not a join in the query), then the parent will care about the value (which in your case would be a nested map in app state)
so if you need normalization, then we need to talk about how to fix your overall approach
how do I query ... I just don't follow because I don't know how to query a value from another ident without a join
ok, then the answer is “yes, you need normalization”…in that case you also need to use proper join query and component factories
ok so instead of runtime-page working with the data directly I need to pull those parts out
right, and I suspect you might have logic that would benefit from a union query…but just guessing
nah probably just lazy. its an overview page showing some stuff about the runtime and then all the other data it reads
There are other options…the runtime has a spot to put “additional refreshes” for the scheduled render…adding the ident of the parent there should do the trick. That’s essentially what you were trying to do with your transact!
trick..but I think an empty tx just ends up being a no-op. No queue processing happens, so nothing gets scheduled.
you could try adding options to schedule-render!
…I think that would work: (app/schedule-render! app {:refresh [ident-of-parent]})
both of those options only work with the default renderer, and I’ve not tested the latter heavily…but I see no reason why that would not work. Sounds like you’re possibly a good use-case for the :only-refresh
…it circumvents the diff analysis and just schedules a targeted refresh.
I should have documented that those options on transact!
are passed through to schedule-render!
Welcome 🙂
Just to be clear, @thheller, :refresh
means “do the full refresh analysis, and include this extra stuff even if it doesn’t look dirty”, and :only-refresh
completely skips the state diff, so is faster when you know what you want to refresh.
For reference, here is the logic: https://github.com/fulcrologic/fulcro/blob/develop/src/main/com/fulcrologic/fulcro/rendering/ident_optimized_render.cljc#L100
Oh, and in the case where you’re not using a factory for that child, the ident/keyword you want to refresh is the one on the parent.
@tony.kay one more thing. I have split up the representation of one ident into 3 smaller ones. basically because the body changes a lot while header/footer don't. premature optimization I know ...
(when object
(html/fragment
(ui-object-header object)
(ui-object-detail object)
(ui-object-footer object)))
but now I can't figure out how to make one "object" component that then does the split?
or maybe its just something else but sometimes this display doesn't update either. although those components always have [::oid 123]
as their ident
so, if they share an ident, the renderer will trigger refresh on them all anyway when the data changes…if their queries differ, then s.c.update might actually short-circuit
I would make one component, and you could push the header/footer out to comps without a query/ident just to get the s.c.u. optimization