This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-09-12
Channels
- # ai (1)
- # announcements (7)
- # babashka (32)
- # beginners (23)
- # biff (9)
- # calva (1)
- # cljs-dev (13)
- # clojure (32)
- # clojure-belgium (1)
- # clojure-chicago (15)
- # clojure-europe (24)
- # clojure-india (3)
- # clojure-nl (3)
- # clojure-norway (55)
- # clojure-uk (4)
- # clojurebridge (1)
- # clojurescript (5)
- # core-async (17)
- # data-science (9)
- # datomic (29)
- # events (3)
- # fulcro (16)
- # graalvm-mobile (4)
- # helix (15)
- # hyperfiddle (74)
- # introduce-yourself (1)
- # jobs (4)
- # kaocha (12)
- # leiningen (27)
- # lsp (16)
- # shadow-cljs (6)
- # spacemacs (20)
- # sql (27)
- # squint (7)
- # tools-deps (29)
- # vim (2)
- # xtdb (10)
Hello, all! I have a question about how to access data from another component — this is for the toy RSS feed reader I built at https://github.com/realgenekim/rss-reader-fulcro-demo. I’ve been attempting to rewrite it so that the components are better organized, as per @tony.kay suggestions many years ago.
I have a new component StoriesTOC
that stores all the RSS stories — all-stories
is the list of stories, current-story
is the “currently selected and displayed story”, and stories-search-results
contains a subset of stories that matches a search. In the screenshot, it is shown on the left-hand side.
(comp/defsc StoriesTOC
[this {:ui/keys [all-stories stories-search-results
current-story
filter-input]
:as props}]
{:ident (fn [x] [:component/id ::StoriesTOC])
:query [{:ui/all-stories (comp/get-query StoryTitle)}
{:ui/current-story (comp/get-query FullStory)}
{:ui/stories-search-results (comp/get-query FullStory)}
:ui/filter-input]
:initial-state {:ui/filter-input ""
:ui/all-stories []
:ui/stories-search-results []}
:componentDidMount (fn [this]
(log/warn :StoriesTOC :mounted!)
(df/load! this :story/all-stories StoryTitle
{:target [:component/id ::StoriesTOC :ui/all-stories]
:without [:ui/number]
:post-mutation 'com.example.model.mutations/create-prev-story-next-cache}))}
(log/debug :StoriesTOC :current-story current-story :filter-input filter-input)
(div
,,,
I am trying to use all of those data elements in a new component, in a StoryIndex
component in the upper-right. I don’t want to df/load!
it into this component, since it’s already in StoriesTOC
. What is the best way that I can use those data elements in StoryIndex
? Shown below is my code, which is obviously not working.
(comp/defsc StoryIndex
[this {:ui/keys [all-stories]
:as props}]
{:query [
{:ui/all-stories (comp/get-query StoryTitle)}]
:ident (fn [] [:component/id ::StoryIndex])
:initial-state {:ui/all-stories []}}
(dom/div
(log/warn :StoryIndex :props props)
(dom/p (count all-stories))))
Many thanks!ok, first a comment in general:
Coupling the load with ui mount isn’t ideal, and this scenario is a prime example. What if the mount order of the two different “uses” of this shared data changes. If the data’s availability is tied to the mount, then so is your data lifecycle. Not necessarily a great coupling, but we will ignore that for now.
In terms of data reuse: it is normalized. I can see several possible ways you could shared this data:
1. You could give the different components the same ident, and make sure both are in initial state (this will cause your “singleton” ident to appear in multiple places in your UI data graph). This will have the effect that any number of components in your UI share exactly the same state from the state database. If you have some differences between then, use different :ui/…
props to distinguish those differences.
2. If you really want different components, then instead of targeting your load to a component, leave the load result at a root key (in your case :story/all-stories
) and use a link query in all of the places you need those: :query [:ui/boo :ui/bah [:story/all-stories '_]]
and let Fulcro pull the root key into each component that needs that data
you could also add :story/all-stories
to the shared keys in your application config, and then they would appear from (comp/shared this)
.
And of course you can also target a load to multiple places. In this case that’s the least desirable solution I think, but look at data targeting multiple-targets
, and tell it the locations at which the result should go
OMG, this is so fantastic — it works as promised! @U0522TWDA Would love to have this use case show up in the tutorial. It’s so handy! (I’m adding it to my TODO list.)
Thank you for the tip, Gene! Can you see where in the tutorial this would fit? Also, regarding you preferred solution to the original problem, have you seen https://blog.jakubholy.net/2020/fulcro-divergent-ui-data/#_multiple_ui_views_of_a_single_data_entity ? Would you improve it somehow?
Thanks for the help on the above, @tony.kay — works brilliantly!
You mention that df/load!
shouldn’t be done on :componenDidMount
— where/when is the best place to do the data load?
I see in the docs that you do it on application initialization — is that still the preferred place?
(defn ^:export init []
(app/mount! app ui/Root "app")
(df/load! app :friends ui/PersonList)
(df/load! app :enemies ui/PersonList)
(js/console.log "Loaded"))
https://clojurians.slack.com/archives/C68M60S4F/p1694555095460369?thread_ts=1694554504.399499&cid=C68M60S4FI can only point to https://fulcro-community.github.io/guides/tutorial-minimalist-fulcro/index.html#_when_to_load 🙂 Improvement suggestions welcome
Nice! Thank you! (don’t know how I got into the habit of putting df/load in component mount…). PS: if you have a place where you’re putting in fulcro code demos in a “book format” or “devcard format”, please let me know — I’d love to add to it!
Did you mean https://fulcro-community.github.io/guides/tutorial-minimalist-fulcro/index.html?
@U05ML354JLU is something wrong with my link? It points to the same page, but to a particular section of it. Or does it not work for you?
No. My fault. I didn’t track the thread carefully and missed the reference. Sorry for the misfire.
Data load is sometimes tied to the lifecycle of a single component, in which case it is harmless to leverage React as a mechanism for triggering a load. In the cases where rendering doesn’t have a 1-to-1 correspondence with data lifecycle, then IMO there should be some code unit that is responsible for that logic: loading, caching, reloading, clearing, updating. That is not part of Fulcro or React. It is part of your application, and as such, is something you should think about and design. Perhaps a state machine makes sense. Perhaps just hooking function calls onto user-driven events. Perhaps it is a code unit that is triggered by react, but has logic of its own. For example, imagine you put a call to some mutation you’ve written called ensure-data-loaded
which knows to 1. check the app state to see if the data is there, possibly checking if it is too stale. 2. possible issues a load if it isn’t there, with well-known marker logic. 3. all UI components that plan on using that data can now have their mount trigger a call to that function. If you’re careful about the logic, then any number of components can call it, and it will trigger at most one load.
I can only point to https://fulcro-community.github.io/guides/tutorial-minimalist-fulcro/index.html#_when_to_load 🙂 Improvement suggestions welcome