This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-03-18
Channels
- # announcements (31)
- # asami (11)
- # aws (33)
- # babashka (30)
- # beginners (69)
- # calva (1)
- # chlorine-clover (10)
- # cider (3)
- # clj-kondo (24)
- # cljdoc (19)
- # cljs-dev (1)
- # cljsrn (2)
- # clojure (104)
- # clojure-australia (4)
- # clojure-dev (29)
- # clojure-europe (202)
- # clojure-germany (36)
- # clojure-nl (6)
- # clojure-poland (3)
- # clojure-serbia (6)
- # clojure-spec (18)
- # clojure-uk (32)
- # clojurescript (36)
- # conjure (1)
- # core-async (6)
- # datomic (15)
- # duct (1)
- # emacs (11)
- # fulcro (24)
- # graalvm (12)
- # jobs (3)
- # juxt (1)
- # kaocha (2)
- # keechma (4)
- # lsp (1)
- # malli (102)
- # meander (17)
- # off-topic (16)
- # pathom (8)
- # re-frame (12)
- # remote-jobs (7)
- # rewrite-clj (72)
- # shadow-cljs (27)
- # sql (26)
- # tools-deps (8)
- # vim (3)
- # xtdb (28)
- # yada (5)
components re-render when their data changes. If you don’t see a refresh, it is likely that you don’t have things correctly connected. Your query+ident+state must compose.
Weird -- in the "element" tab of Fulcro Inspect, I can clearly see the component's ident. And in the "DB" tab I can clearly see that ident's corresponding DB entry has changed. I don't get it why the component is not re-rendered.
Basically, I have a :onClick #(df/refresh! this)
on the triangle. Before the user clicks on the triangle, the folder component already has the :dir/id
and :dir/name
loaded in [:dir/id "folder_id"]
DB entry. Clicking on the triangle,should trigger loading of the folder's children, in the form of the :dir/sub-dirs
field.
So technically a new field :dir/sub-dirs
is added to the DB entry of [:dir/id "folder_id"]
.
I tried manually adding :refresh
or :focus
to the df/refresh!
but the result is the same -- somehow the folder component is never re-rendered (shouldComponentUpdate is never triggered)
Ahh, could it be because of the recursive query? Since I write statically {:dir/sub-dirs 1}
, fulcro wont check the :dir/sub-dirs
field in the second level even when it is now loaded
Looks like I need to use Dynamic queries, and every instance of Dir
needs to have its own qualifier?
And to do that do I need to make factories dynamically? Like:
(defsc Dir [..]
{..}
...
(for [sub-dir sub-dirs]
((comp/factory Dir {:qualifier (comp/get-ident this)}) sub-dir))
...
)
That seems quite wild to meBTW I just found that (comp/computed-factory)
doesn't preserve the meta attached by (comp/factory)
and thus breaks dynamic queries. Perhaps a bug
@tony.kay Correct me if I'm wrong but after some further investigation I think what I'm trying to implement is perhaps very difficult in current Fulcro. I realize that much of the power of Fulcro comes from its ability (and thus requirement) to be able to build the entire query tree at any time. That's why we talk about dynamic queries as something to be considered carefully. If we were doing plain AJAX, we wouldn't even draw our attention to whether a query is "dynamic" or "static", because a query is just a query in that case. But in Fulcro, a query is not just a query, it's also part of the entire query tree that needs to be rigorously maintained and indexed.
And to implement a lazy tree, what we are really doing is constantly modifying the query tree -- dynamically. Basically every instance of Dir
needs its own dynamic query. Since these dynamic queries need to be tracked in the DB, we need to give them each a unique qualifier.
However, right now this is extremely hacky to achieve. Fulcro at the moment assumes that each query is associated with a component class, or at least a factory (via qualifier). In order to implement the lazy tree we need each component instance to have their own mutable query.
Maybe I don't quite get your problem ... On your Ui Component you can make an unbounded query for :dir/sub-dirs And then you can modify the query for your load. You can add a component just for loading.
(defsc MyDirLoadHelper [_ _]
{:query [:dir/id {:dir/sub-dirs 1}]
:ident :dir/id})
(declare ui-dir)
(defsc Dir [this {:dir/keys [id sub-dirs]}]
{:query [:dir/id
:dir/name
{:dir/files [:file/id :file/name]}
{:dir/sub-dirs '...}]
:ident :dir/id
:initial-state
(fn [{:keys [id name]}]
{:dir/id id
:dir/name name
:dir/files []
:dir/sub-dirs []})}
(dom/div
(dom/button
{:onClick #(df/load! this [:dir/id id] MyDirLoadHelper)}
"Load sub-dirs")
(map ui-dir sub-dirs)))
(def ui-dir (comp/factory Dir {:keyfn :dir/id}))
Or you could use the :update-query
on load!
{:update-query
(fn [query]
(-> query
(eql/focus-subquery [:dir/sub-dirs])
(assoc-in [0 :dir/sub-dirs] 1))))}
(You can probably improve this)Right, so, in general you want to separate in your mind the loading of data from the UI use of that data. Fulcro’s UI for this is dead simple and trivial…I agree that @U4VT24ZM3’s approach is probably better. Just use full recursion, and only fill out the nodes that need more data on demand (user folding open an item). The folding open can be component local state if you have a tree that for whatever reason shows the same stuff twice (and you want them to not fold open at the same time).
“fold open” being just the difference in the rendering..the subtree, of course, will exist in both places since it is a normalized db
I like @U4VT24ZM3’s approach. However, the downside is that I have a "refresh view" bottom somewhere near the root of the entire view, and right now it simply does (comp/refresh! this)
. If I write full recursion, then I probably need to have a mechanism to prune the loading query and make sure that clicking on the "refresh view" won't load the entire file system.
@tony.kay Like you said, I need to separate query loading from query definition. This separation of course comes with the bitter inconvenience of not being able to simply write (comp/refresh! this)
in all the places. 😄
What exactly does this refresh do? You could load every dir in your normalized db again:
(defn refresh-dir [appish {:dir/keys [id]}]
(df/load! appish [:dir/id id] Dir {:without [:dir/sub-dirs]}))
(run! refresh-dir (get (app/current-state) :dir/id))
You would have to ask @tony.kay about how to load bulk data best. Never done it before.
@U4VT24ZM3 The refresh view button is supposed to reload the entire tree view, updating the files according to the changes in the file system. I think what you wrote should work with some minor performance improvements. I would perhaps call refresh-dir
recursively in order to avoid trying to load dirs (and their descendents) that are no longer there. Also I would combine all df/load!
into one batch query.
@U4VT24ZM3 These are very helpful suggestions. I appreciate your help!
I have not had time to finish the batched networking. There’s work in process on this branch here: https://github.com/fulcrologic/fulcro/commit/35a47b26f366833de26b496a1902b5232dba1431 So, at the moment, you’d just have to scan the “open tree” and issue loads for each level you’re interested in.