Uploading part 4 of Grokking Fulcro now. This episode dives into the internals of the humble comp/factory
. This is some low-level detail that you might find interesting, or just obscure. Either way, you’re living in a React ecosystem, and seeing what hoops we’re jumping through to make that work is possibly useful to the more serious developer (or Fulcro contributor??? 😉 ).
Awesome! I am just at the first video but I have already benefited from and applied what I learned there. Thank you!
This series plus the original 24 part playlist are really stellar and great resources.
Really enjoying this series, looking forward to the next ones.
Good to hear. I personally like knowing how things work, so they’re fun to make from that point of view. A lot of the little minutiae are really mostly useful for super-advanced use-cases (e.g. I’ve never made a custom factory in any of my apps, but I could see the need to at some point….I honestly forgot about props middleware until I saw it while making the video yesterday, etc.).
It should be visible in the next 30 mins. YouTube is processing. Lower quality will be first, and the HD will show up sometime later.
For components that only concern themselves with layout (some divs + css), is the convention to use a query'less defsc
or just function?
I asked the same question, https://webcache.googleusercontent.com/search?q=cache:F1UQLUsATpUJ:https://clojurians-log.clojureverse.org/fulcro/2020-12-29+&cd=1&hl=es-419&ct=clnk&gl=cl > It’s all just function calls anyway. If there’s a common layout/formatting concern there is no problem at all putting that in a function, or even a component without query if you want shouldComponentUpdate optimizations.
Is it possible to mix ident and route-segment, something like:
:ident (fn [] [:foo/bar bar-id])
:route-segment ["foo" :bar-id]
?(defsc ShirtList
[this {:keys [shirt-list/shirt-names]}]
{:query [{:shirt-list/shirt-names (fcomps/get-query ShirtName)}]
:ident (fn [] [:shirt-list/color shirt-color])
:route-segment ["shirts" :shirt-color]
:initial-state {:shirt-list/shirt-names []}
:will-enter
(fn [app {:keys [shirt-color]}]
(dr/route-deferred [:shirt-list/color shirt-color]
#(load! app :shirt-list/shirts ShirtName
{:fallback `errors-mutations/check-response
:params {:shirt/color (keyword "shirt.color" shirt-color)}
:target [:shirt-list/color shirt-color :shirt-list/shirt-names]
:post-mutation `dr/target-ready
:post-mutation-params {:target [:shirt-list/color shirt-color]}})))}
(dom/div
(map #(ui-shirt-name %) shirt-names)))
Hey, I think I may have figured it out! Like you said, return a unique id in the pathom query. Took me a while to see how that would work :man-facepalming: The example component above is what I've been working with (incorrectly), but now I understand your earlier suggestion. Thanks!
I was confused because the component already knows about the shirt color because it's a part of the route params. It wasn't obvious to me that the solution was to have the resolver echo it back. I thought the component should just be able to make use of it
Right. The code above looks fine - you get the color from the route params and use it for the target-ready
and to store the data in the right place of the client db. But as you point out, the data stored also need to include the color so either the resolver has to "echo it back" or you need :pre-merge
or some post-mutation/action to add it in. (Though perhaps pre-merge cannot "see" it...)
If you can think of any way to make the documentation clearer on this point, please suggest it!
> I was confused because the component already knows about the shirt color
Yes, the component inside will-enter
"knows" it but we also still need to make sure that it will be included in its props because ID always needs to be there. And Fulcro will not magically add it for you even though it could, in theory.
Thanks again for your help @holyjak
my pleasure 🙂 FYI I to those interested enough, I offer pair-programming help. The first hour is free without any obligation to continue after that. So if you run into some hairy issues, you can consider that 🙂
Awesome! I will definitely keep that in mind
Hi, I'm starting in fulcro3 and I wanted to create routing in my app, I made a quick search and see something as state-machine, right? may you guys have some links that can help understand how is the best way to do this?
FYI, I'm not sure yet that bar-id will be a part of the component's props or query. It's passed to a child component currently
A teaser: I am creating a small library fulcro-troubleshooting
to help detect problems earlier and find root causes faster. Stay tuned...
(I should have mentioned that design help would be appreciated 😅)
Up to you. Defsc makes it more structured which is good for the React dev tools Components view and error messages.
the code is perfectly valid. But the keyword is I think arbitrary, it does not need to match the id prop. It just means "this is placeholder"
look at Dynamic Routing in the fulcro book
ok , thanks
How familiar are you with Fulcro? If quite new then I'd really recommend following https://github.com/fulcro-community/guides/blob/main/learning-fulcro.adoc#tip-1-keep-it-simple for now
I'm starting with fulcro, usually I used reframe.
Then I would certainly wait.
Have you read https://github.com/fulcro-community/guides/blob/main/minimalist-fulcro-tutorial/index.adoc ?
I haven't read it, I start with yotube videos and fulcro book
I strongly suggest you do. Though I am obviously biased as the main author 🙂
I'll read it, thanks very much for recommendations
Ok. I'm stumped. I've been working on this for 3 days now. I need help. Here's my UI tree (React Native):
(defsc Category [this {:category/keys [id label] :as props}]
{:ident (fn [] [:category/id id])
:query [:category/id :category/label]}
(ui-view {:marginTop 50}
(ui-text {:style {:fontSize 20}} (str id " " label))))
(def ui-category (comp/factory Category {:keyfn :category/id}))
(defsc CategoryList [this {:list/keys [id categories]}]
{:ident (fn [] {:list/id id})
:query [:list/id {:list/categories (comp/get-query Category)}]}
(ui-view {}
(ui-text {:style {:fontSize 32}} "CategoryList")
(when categories
(map ui-category categories))))
(def ui-category-list (comp/factory CategoryList))
(defsc Root [this {:keys [categories] :as props}]
{:query [{:categories (comp/get-query CategoryList)}]
:initial-state {:categories {}}}
(ui-view {:style {:marginTop 100}}
(ui-text {:style {:fontWeight "bold" :fontSize 32}} "Root")
(ui-category-list categories)))
and here are my resolvers:
#?(:clj
(pc/defresolver categories-resolver [env {:list/keys [id]}]
{::pc/input #{:list/id}
::pc/output [{:list/categories [:category/id]}]}
{:list/categories (queries/get-all-categories env nil)}))
#?(:clj
(pc/defresolver list-id-resolver [env _]
{::pc/output [{:categories [:list/id]}]}
{:categories {:list/id :categories}}))
with id
and label
defined as per the Fulcro RAD demo. I don't know why this does not render the individual categories.@hadilsabbagh18 https://clojurians.slack.com/archives/C68M60S4F/p1614110142063000 would have showed you the ident mistake. Though I guess I need to replace dom/div and p with something else for native... Any suggestions?
Hi @holyjak. I am not thrown off by regular React. My biggest problems seem to be grokking the root component of my UI tree. I just don't know how to connect it to my load!
I just missed it into your Fulcro Troubleshooting Decision Tree, that's all. I've been reading carefully.
> My biggest problems seem to be grokking the root component of my UI tree. I just don't know how to connect it to my load! Could you elaborate? Your load! as shown above, with the data coming back, seems correct, no? So is normalization of the (correct) data into the client DB not doing what you expected?
That's the problem though, bar-id does not exist in the component's props. I want different instances of the component in Fulcro's db indexed by a dynamic route param
where is the load?
I expect to see a (df/load! app :categories CategoryList)
That is what I have.
(df/load! @SPA :categories root/CategoryList)
SPA should not need to be an atom, but that is ok…and in Inspect, so you see the load work and return the right thing?
Here is the screenshot of Fulcro Inspect. :list/id
looks wrong to me...
The nli
key bothers me.
that is not what I asked
please read the question carefully
In the network tab, what do you see?
No the load does not return the right thing.
That doesn't have anything to do with the segment I think. Look at deferred routing - it can see route params and mark a particular ident as ready for display
Oh, sorry.
please show
RequestSend to query
[{:categories
[:list/id {:list/categories [:category/id :category/label]}]}]
Response
{:categories
{:list/id :categories,
:list/categories
[{:category/id #uuid "ffffffff-ffff-ffff-ffff-000000001000",
:category/label "Landscaping"}
{:category/id #uuid "ffffffff-ffff-ffff-ffff-000000001003",
:category/label "Winter"}
{:category/id #uuid "ffffffff-ffff-ffff-ffff-000000001002",
:category/label "Fall"}
{:category/id #uuid "ffffffff-ffff-ffff-ffff-000000001001",
:category/label "Spring/Summer"}]}}
SorryI didn't look there, so there's something wrong with my rendering, correct?
no worries…but that looks fine, so not a problem with network API
what is all-categoeies?
and why is it in state?
nothing in your code mentions it, yet it is in your DB screen shot
which means something isn’t right in terms of what you’re showing me.
OH….your ident function is totally hosed
:ident (fn [] {:list/id id})
The component obviously needs a unique prop for you to be able to distinguish the different instances, no?
is wrong
idents are vectors
Ok. Thanks for your help.
Use either :ident :list/id
(which will write it correctly FOR you), or (fn [] [:list/id id])
I still don’t understand why you have all-categories in state map, but that is the source of your mis-normalized data
Ok, that fixed it! Thanks Tony.
all-categories
Is an attribute which I used previously. I will try to restart and then remove it if it's still there...It's still there. I will work on that. Thank you again.
See the example here https://book.fulcrologic.com/#_deferred_routing
> The component obviously needs a unique prop for you to be able to distinguish the different instances, no? Right, so I would have to set this prop using the route param, somehow. Like a computed prop, maybe? This isn't something I can query for. I've played around with this a bit and I don't think it's possible. I'll have to think of a different approach. Thanks
🙏 I am looking for beta-testers for https://github.com/holyjak/fulcro-troubleshooting Please try it out and let me know how it works! 🙏
I’m in! 😄
Much appreciated!
Maybe if u explained more your use case? U want unique instances but they don't have unique data?!
I'm pretty new to guardrails so I'm not sure if handling fspecs correctly. I expect both of these functions to be valid. Instead, the guardrails-f
results in a somewhat weird explanation.
Let's say I have a "shirts" component with a route segment like ["shirts" :color]. The value of :color is a param, e.g. a where clause, to a pathom query. The shirts component will have a collection of shirts which will be unique per shirt color, but it doesn't have its own id. I don't think it should be a singleton. Of course, I could be missing an obvious pattern here, but I can't think of an appropriate ident for the component other than the value of its route segment. Does this make sense? Thanks a lot for helping me work through this
Each unique thing needs a unique ID, I believe. If it doesn't, make one up, eg in your pathom resolver. Cannot color be the ID?
BTW https://blog.jakubholy.net/2020/troubleshooting-fulcro/ could have perhaps helped
That's what I was hoping for. However, this :ident (fn [] [:shirts (-> this dr/current-route second)])
doesn't seem to work properly. The component doesn't seem to respond correctly to route changes.