Fork me on GitHub

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??? 😉 ).

Jakub Holý (HolyJak)10:02:20

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.

👍 24

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,;cd=1&amp;hl=es-419&amp;ct=clnk&amp;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.

Jakub Holý (HolyJak)16:02:59

Up to you. Defsc makes it more structured which is good for the React dev tools Components view and error messages.


Is it possible to mix ident and route-segment, something like:

:ident (fn [] [:foo/bar bar-id])
   :route-segment ["foo" :bar-id]


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

Jakub Holý (HolyJak)16:02:02

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"


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

Jakub Holý (HolyJak)19:02:17

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

Jakub Holý (HolyJak)19:02:49

The component obviously needs a unique prop for you to be able to distinguish the different instances, no?


> 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

Jakub Holý (HolyJak)20:02:01

Maybe if u explained more your use case? U want unique instances but they don't have unique data?!


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

Jakub Holý (HolyJak)21:02:50

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?


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.

Jakub Holý (HolyJak)06:02:50

No. Make latest when you insert the data in your client DB. How else could it be inserted to a unique place?


(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 []}
   (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]}})))}
    (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!

🎉 3

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

Jakub Holý (HolyJak)12:02:01

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...)

Jakub Holý (HolyJak)12:02:30

If you can think of any way to make the documentation clearer on this point, please suggest it!

👍 3
Jakub Holý (HolyJak)12:02:22

> 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 @U0522TWDA

Jakub Holý (HolyJak)12:02:20

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?

Jakub Holý (HolyJak)16:02:35

look at Dynamic Routing in the fulcro book


ok , thanks

Jakub Holý (HolyJak)16:02:13

How familiar are you with Fulcro? If quite new then I'd really recommend following for now


I'm starting with fulcro, usually I used reframe.

Jakub Holý (HolyJak)16:02:02

Then I would certainly wait.


I haven't read it, I start with yotube videos and fulcro book

Jakub Holý (HolyJak)16:02:13

I strongly suggest you do. Though I am obviously biased as the main author 🙂


I'll read it, thanks very much for recommendations

👍 6
Jakub Holý (HolyJak)16:02:09

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 😅)

🎉 6

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:
   (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)}))

   (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.


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.


Oh, sorry.


please show


RequestSend to query
  [:list/id {:list/categories [:category/id :category/label]}]}]
 {:list/id :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"}]}}


I 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})


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.

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.

Jakub Holý (HolyJak)21:02:45

@UGNMGFJG3 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 @U0522TWDA. 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.

👍 3
Jakub Holý (HolyJak)09:02:50

> 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?

Jakub Holý (HolyJak)19:02:16

🙏 I am looking for beta-testers for Please try it out and let me know how it works! 🙏


I’m in! 😄

❤️ 3

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.