This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-01-17
Channels
- # adventofcode (2)
- # beginners (153)
- # cider (14)
- # clara (9)
- # cljs-dev (8)
- # cljsjs (1)
- # cljsrn (4)
- # clojure (124)
- # clojure-dev (9)
- # clojure-france (18)
- # clojure-greece (22)
- # clojure-italy (11)
- # clojure-nlp (5)
- # clojure-russia (9)
- # clojure-spec (21)
- # clojure-uk (40)
- # clojurescript (82)
- # core-async (12)
- # cursive (3)
- # data-science (2)
- # datomic (225)
- # devcards (8)
- # docs (2)
- # duct (1)
- # emacs (18)
- # figwheel (2)
- # fulcro (117)
- # graphql (13)
- # hoplon (10)
- # jobs (7)
- # jobs-discuss (7)
- # keechma (8)
- # leiningen (4)
- # off-topic (16)
- # om (2)
- # om-next (3)
- # perun (11)
- # precept (4)
- # re-frame (24)
- # reagent (2)
- # remote-jobs (8)
- # ring (2)
- # ring-swagger (9)
- # rum (42)
- # shadow-cljs (8)
- # spacemacs (3)
- # specter (7)
- # uncomplicate (10)
- # unrepl (58)
- # yada (9)
@tony.kay where was your book again?
is there a way to :target
a load to have the return data merged into the root of the state db, as opposed to underneath the load key/some specified target key?
yep get that. let’s say my call to load for :user
with a query of [:user/email :user/first-name]
receives back a map like {:user/email "
.
when fulcro merges that load back into the state map it’ll look like {:user {:user/email "
.
makes it slightly annoying to join throughout components, as I’m constantly doing [{[:user '_] [:user/email :user/first-name]}]
.
the alternative – and this is the crux of my question, I just don’t know how to accomplish it – is to merge all the properties returned by the :user
load into the root of the state tree, so there’d be :user/email
and :user/first-name
keys at the root of the state tree, as opposed to nested under the :user
key.
that’d make querying for that returned data much easier, especially when its nested maps of properties (e.g. a user’s team member’s likes)
you just need to make sure that a top-level refresh happens if such shared data changes...and in this case that's easy, so it's a very good fit.
That support has been in Om Next and Fulcro since very early (2015), but not well-documented.
note that shared-fn
is on root props not the state database. Therefore anything you want to share from root has to be queried at root.
hey @tony.kay what should I do if I find a typo in the book? is there a way to submit a pull request?
@wilkerlucio thanks, i didn’t realize it was in the main app
no problem 🙂
I was bumping our app to 2.1. We had a homegrown routing component but I wanted to use the official Fulcro router. I got this example from the book working in my app http://book.fulcrologic.com/#_a_complete_ui_routing_example But for some reason switching to a new route (via clicking the links) does a double render. - First render from root is called with the current route - Second render from root is called with the new route This doesn’t seem like the way it should be, any ideas what could be wrong?
Also, was mutation logging turned off intentionally? I don’t see it anymore.
Clicking on current tab causes a single re-render too, that seems very strange.
@wilkerlucio @currentoor it is all on mainline development now (the book)
ah i see
ups, sorry, I just saw the branch there and assume was been used
@currentoor doing a transact will always render...what is your issue in particular?
I added your example from the book into my AdStage app, and switching to a new route causes render to be called twice (once with the old route value and once with the new value)
I added that example into the root component of my app, should switching to a new route cause render to be called twice?
i guess i can verify with the example application
i just assumed that was not correct behavior
ok, so it is possible there is a double call of render...but if the logic makes it into render, then some data changed
there are two primary ways render gets queued: transact will queue things up, and swap!
s on the state atom queue a root render/reconcile.
scheduling a render does happen on an animation frame rate (throttled to 60fps), so a combo of transacts and swaps could trigger a dual render pretty easily.
oh so maybe this isn’t a problem then
so a single transact
and the swap!
s inside it can trigger separate renders?
so, it might be non-optimal. The rendering code rewrite was pretty invasive in 2.x. The fact that you're seeing old state then new is strange.
knowing what changed on the first render, compared to what changed on the second would be helpful
i’m just worried this will get expensive when i have a bunch of charts already rendered on the screen
your render body will not execute unless something actually changed. So, double-render should not happen unless there is a data change.
i think only the diff is in that screenshot 😅
i should have circled it or something
> so, one diff should be one render no matter what then it might be something i’m setting up incorrectly, perhaps i should verify that this happens in the example application?
shouldComponentUpdate
returns false if the data hasn't changed. Where is your print?
the print is inside the render body of the root component
did not include more than once
ah, that's a clue. force-root-render!
is an exception: it forces the UI to update everything
#(prim/transact! this `[(r/route-to {:handler :main})])
is that what you mean? i just copied the example from the book exactly
the new optimal render path has to do a forced update of a given component in case it was just a component-local state change. This would cause you to see the old props, then the new in rapid succession.
The forced update only affects the component, as all child shouldComponentUpdate calls will return normally so that children won't render.
just verified and you’re right!
only the root component render body was being called, not it’s children
At the moment, anything that is targeted at root will cause a double-root node render
right
The new optimizations only work on components with idents. Root doesn't have an ident.
i see
thanks for the help and explanation
I haven't started recommending it, but using a placeholder root that does nothing but delegate to a node with an ident has a couple of benefits. One would be that it will be so lightweight that this could never matter; however, the other advantage is that nothing ends up in the root node but a single keyword (like :ROOT
) and your tables.
can’t that be done by Fulcro?
i see
and that is a main disadvantage...since nothing is in the root node, [ [:rootprop '_] ]
is less useful
I like it, in that it makes it easier to find the stuff you're considering to be your root, since it is all in a map keyed at :ROOT
however, as I pointed out to someone earlier today: shared-fn
can be used to pull info out of the root props and into shared
. That doesn't get broken...you just have to walk the props into :ROOT before pulling data into shared.
> since it would move things that you might be using link queries for if you’re using link queries, it shouldn’t matter if data moves up one more level right?
ident-based queries, no...but root node is not longer root node, so it would make it harder
you could no longer just pull an arbitrary root node value...you'd have to pull everything in :ROOT
and then deal with that...and you would never want to join that with the actual root query, or you'd make a big mess
so you wouldn't get denormalization without then writing some other component to query against the fake root
I see, well I haven’t seen any super complicated roots. Feels natural to write simple roots that delegate to other more complicated components.
but I do have a smallish sample size 😅
careful now, don’t want fulcro to be crucified on twitter…
well we already know fulcro is so offensively good that it might cause someone to piss all over your expensive mechanical keyboard
maybe it should go on the website
@currentoor I have this helper function that I like to use to make roots:
(defn make-root [Root app-id]
(fp/ui
static fp/InitialAppState
(initial-state [_ {::keys [root-state] :as params}]
(merge
{:fulcro.inspect.core/app-id app-id
:ui/root (or (fp/get-initial-state Root (dissoc params ::root-state))
{})}
root-state))
static fp/IQuery
(query [_] [{:ui/root (fp/get-query Root)}])
static css/CSS
(local-rules [_] [])
(include-children [_] [Root])
Object
(render [this]
(let [{:ui/keys [root]} (fp/props this)
factory (fp/factory Root)]
(factory root)))))
I think we talked about that one before @tony.kay, but I think we agreed it's not generic enough for general case, maybe
And @wilkerlucio saves the conversation 🙂
cool thanks @wilkerlucio
well noted, I just bumped this project to fulcro 2.1 today 🙂
yeah, also, the root div
can also be removed
snippet updated