Fork me on GitHub
#fulcro
<
2023-07-10
>
janezj20:07:02

When porting an old cljs app to Fulcro and also when listening https://podcasts.apple.com/us/podcast/e93-frontends-in-2023-with-alex-davis/id1461500416?i=1000619329906 @10:50 where Alex Davis says how he failed with Fulcro I got an idea what would prevent many brain meltdowns. I think the solution is function pathom to get data from reslovers, and has ok-action to update the state.

(pathom SPA {[:device-id 15] [:type :status]} 
  {:ok-action (fn [{:keys [state result]}] (swap! state assoc :result result))}) 
It is like a rest call on steroids - familiar client database - yes, view = f (state) [:result '_] brings data to every component in the tree. Normalized database is the next level.

tony.kay21:07:23

I’m not sure how this affects his particular complaint. The biggest two things he says are that he had trouble figuring it out, which always puzzles me. The second thing is that he says he doesn’t like the “coupling” between front and back…which I don’t get either, since there is almost no real coupling…you can pick whatever you want for the actual implementation. Why do you pick this particular thing?

tony.kay21:07:52

I’m continually baffled that so many people find it so hard to get started with Fulcro. I know there are a number of new-ish concepts, but the overall design is so simple that I really would like to see what people are running into and what they are thinking when they get “stuck”. There is clearly a disconnect between their “expectation” and “reality”.

tony.kay21:07:45

I suspect that they are thinking there’s a lot more magic than there actually is.

caleb.macdonaldblack23:07:31

@U0CKQ19AQ I thought it to be quite magic before understanding more about how it works. I haven’t found the time to try out fulcro yet. However, I’ve spent heaps of time with Pathom and have been pushing it to its limits over the past two years. And also spent a bit of time with Datomic. I’ve grown to love normalized data and eav triplets. I also love deriving data. And I’m eager to play with a client framework/toolchain that is more oriented to solving these sorts of problems. It wasn’t really until I started looking at electric from hyperfiddle that I realized how magic fulcro wasn’t. I think there are a lot of advanced ideas and concepts bundled together in a tool like fulcro that make it intimidating and look magic at first glance. I think the docs could be written to be less intimidating. However, I’m not sure how exactly they could be written differenty. Im really just a guy who has been curious (and previously skeptical) about fulcro but haven’t actually played around with it. I think I know the gist of what it’s trying to do and how it does it, but I can’t really know for sure until I play around with it.

caleb.macdonaldblack23:07:22

As of today I’m now skeptical of electric and more leaning towards fulcro for my next greenfield project with a front end. Until now I’ve been using re-frame and reagent.

tony.kay23:07:02

Well, given that pathom was written to be the back-end of Fulcro (Wilker and I were working together there for a few years) I would expect you to “get it” pretty quickly 😄

tony.kay23:07:23

it’s all the same compositional kinds of ideas.

caleb.macdonaldblack23:07:05

Ultimately I’m blown away with how I can compose data with pathom and I’m eager to build front end code in a similar way. I tried it out crudely and briefly with some Pathom resolvers and reagent code and it felt awesome. I’m not sure I’ve found anything other than Fulcro that will do this.

caleb.macdonaldblack23:07:45

Reframe is great and simple but gets tedious as I’m sure we’ve all experienced

nivekuil00:07:34

one thing I remember that made fulcro hard to learn is that "query" in other contexts usually refers to something crossing the network. I think pathom3's foreign env concept is really nice in this respect because it elides the client/server boundary. Then the fulcro db becomes like a transparent cache for your full-stack logic engine

nivekuil00:07:16

also fulcro is fundamentally simple but has a lot of batteries like form-state that aren't really necessary, and it's hard to tell apart what's important and what's not at first

caleb.macdonaldblack00:07:21

> also fulcro is fundamentally simple but has a lot of batteries like form-state that aren’t really necessary, and it’s hard to tell apart what’s important and what’s not at first This is probably another main factor. With other popular tooling, it’s not uncommon for maintainers to avoid all the little quality of life stuff so as to keep the core library light. Some example that come to mind are: • Re-Frame not interested in implementing debounce fx or http fx handlers • Clojure Core not interested in expanding from mapv, filterv etc for other functions like removev, takev etc • Datomic doesn’t provider helper operations for common things like retract-many, pull-many etc It’s commonly chalked up to not being necessary in the core lib and something easier enough developers to extend themselves. Some example I’ve given aren’t great, for example http in re-frame really is out of scope for the lib. But hopeful I got my main point across

caleb.macdonaldblack00:07:48

I think I recall Pathom3 avoiding some quality of life additions for similar reasons as well

janezj07:07:12

@U0CKQ19AQ > Why do you pick this particular thing? > The second thing is that he says he doesn’t like the “coupling” Till recently I didn't know how to fetch data from backend to the clientdb - how to get data in place where is required and in shape I want. And I think that coupling in podcast means that there is no obvious way how to fetch data with the rest webservice mindset. Here is an example of my current restish load.

(df/load! application/SPA  [:client-id 551] (rc/nc [:sdk :counter-type :scc])
    {:ok-action (fn [{:keys [state result] :as env}]
                   (let  [{:keys [counter-type scc]} (-> result :body vals first)]
                    (swap! state update-in [:component/id :config-dlg] assoc :ui/scc scc)))})
I just want to force data directly to the component, in the place where needed. Quick hack. Which will make me all sort of problems because the data is not normalized, but in this case I just want to survive till the first refactoring. I don't have the complete design of the app in my head. I am just solving the biggest problems. And in next iteration I will be able to figure out better model and remove some hacks.

1
nivekuil17:07:39

df/load is definitely the hardest function to understand and also the only one you need to IMHO

nivekuil17:07:43

I think it might be able to be removed completely if fulcro were designed for pathom3 from the outset

tony.kay18:07:32

I see. So yes, if you have a REST service mindset that is a blocker. If you’re starting a brand new app and have a legacy REST back end then there is no immediate good story, because the remote that comes with it by default expects you to be using an EDN-based graph API using EQL. Pulling data in from REST endpoints does require some extra work in that case, and even your load helper would not really aid in that case. If you have a mess of REST garbage and don’t want to add back-end support for improving that then Fulcro is a poor choice. Actually, that is a good addition to @U0522TWDA’s list of where Fulcro is a bad fit. The pain in making legacy REST work well with Fulcro isn’t worth it. I’d always add a new back-end that supports EQL, otherwise you don’t really get any benefit at all. Hand munging REST data is a nightmare, and is 90% of the reason Fulcro exists…to do away with it.

👍 1
tony.kay18:07:43

But if you just want a generic “load data” helper function then you can write exactly what you’re talking about @UQ5QFFX54 yourself, and that speaks to the fact that I am actually trying to strike the balance @U3XCG2GBZ is talking about: Not including things that are beyond core. I originally had these things all split out in many libraries (e.g. client, server, i18n, etc.) and I agree that the “Clojure” ecosystem way of doing it would have me also put routing, form-state, etc in their own libraries. The only advantage to that is making it “look right” to the community, which is valuable in itself, but way more work than I’m willing to do. I have 16+ libraries already, and it is a pain to do releases of lots of things. So, I chose to fold the stuff I use regularly into the main distribution, since cljs can do dead code elim, and it really makes little difference in the runtime.

tony.kay18:07:48

Untangled had a “load data” function, but I felt it was too confusing to use without a big swiss army knife of options, and the current load support is my attempt to find the middle ground. Having to write your own helper to get arbitrary data loads that may not normalize correctly is left as an exercise for the user specifically because it is a more advanced thing. So, yeah, if you’re not willing to start with and use the basic feature (auto-normalization based on component definitions) then what’s the point of using Fulcro at all?

❤️ 1
janezj19:07:54

In my opinion direct pathom load (in this case load!->ok-action->swap) is like safety net, when you don't know how to bring data to client, just load them directly. After discovering this approach I fill much more confident, that I will be able complete the project. And it seems to me to be easier to start with Fulcro, to load data in more direct and familiar way. Learning from own mistakes and making progress is much better than being stuck. When looking back I thing I would understand the principles much easier knowing this technique or similar at the beginning of my journey. Current load! with nc/rc is awesome, and playing with :target option just makes me think how to redesign the app to work without ok-action.

janezj19:07:55

"I am not smart enough" and "Tony is a genius" are in the podcast . It is so familiar to me, I can really relate to the fillings. 🙂

tony.kay19:07:29

I think it is more likely the case that “Tony failed to get the points across”…

janezj19:07:22

Oh no, this are the exact thoughts I have regularly.

caleb.macdonaldblack21:07:10

> and that speaks to the fact that I am actually trying to strike the balance It’s challenging to know where that balance is. Or even if it needs to be split up like the other libraries I mentioned. As @U0CKQ19AQ mentioned, splitting it out creates a bunch of new problems. I suppose the problem I’m trying to solve (or more accurately curious about) is Fulcro being intimidating and mysterious when looking at it on the surface. Perhaps some of the main reasons it’s intimidating, just aren’t worth the trade-offs. While we can find many examples of great tooling that is easy to learn, we can also find examples of great tooling that isn’t easy to learn, but absolutely worth learning.

nivekuil00:07:45

> So, yeah, if you’re not willing to start with and use the basic feature (auto-normalization based on component definitions) then what’s the point of using Fulcro at all? @U0CKQ19AQ I don't think people are drawn to fulcro because they're thinking "I have a normalization problem" -- that's the insight fulcro offers, not the sales pitch. I'd guess it's more like "here's this cool thing someone spoke highly of on the internet for doing web dev, I wonder what makes it cool?" A proper intro would have to motivate the problem rather than just describe the solution. You can't just tell people that "you hate web dev because your state isn't normalized", have to show it somehow

tony.kay00:07:01

@U797MAJ8M solid point. I’m not much into cheerleading. So, unless the community wants to help with that particular problem, it isn’t going to get better 😕

Jakub Holý (HolyJak)21:07:51

I will leave you with 2 slides from my London Clojurians talk 🙂 Of course they both come with detailed explanations…

Jakub Holý (HolyJak)21:07:42

Regarding the “load some data and force them to a particular place”, you can trigger df/load! with a rc/nc and a post-load mutation to put the data where you want them… Providing a convenience function that makes it easy to fetch data from a custom query and place them somewhere in the state, like you proposed, would I am afraid prevent people from really digging in and trying to understand fulcro and how to do this properly.

janezj14:07:07

@U0522TWDA I agree with you. load! is great, just ok-action is not very obvious (I found out how well designed it is when I was starring into finish-load!). At the beginning of this thread I was not aware how much can be done with rc/nc + load! combo. And that playing with :target can give some ideas to fix some details and do it properly without hacks load! is in my case problematic because I am loading a lot of data ( IoT ). But I am monitoring just one device at time. And when loading into normalized table it stores all data for all observed devices. So I wanted to push everything under one key. I could also delete not required data the later is probably better.

👍 2
Jakub Holý (HolyJak)12:07:02

> But I am monitoring just one device at time. Then you could make the component static, with st. like :ident (fn [] [:component/id :my-current-device]) and have another top-level prop to keep track of the ID. Perhaps would need to do some more work to drop data before loading new device, but your hypothetic mutation switch-device could do that…