Fork me on GitHub
#re-frame
<
2021-08-14
>
pinkfrog15:08:58

Noob quesiton, why use rf/dispatch to call functions? Why not directly invoke some functions?

p-himik15:08:46

I assume you mean something like a not built-in :call-fn effect and not "why use dispatch at all". To simplify views, make their interaction with the outside world more precise (one way to get the data, one way to change it), instrument such calls.

pinkfrog15:08:19

I was referring to the (rf/dispatch [:some-event ]). Why not diretctly call (some-event-func)

pinkfrog15:08:11

Only thing I can come up is the pub/sub functionality. But is there really such a need?

p-himik16:08:53

I can't come up with succinct answer to that. I would suggest reading through the documentation at https://day8.github.io/re-frame/re-frame/ Namely, the main page and the "The Basics" and "Mental Model Omnibus" sections on the left. All while keeping in mind the question: "Would just calling a regular function allow me to do that?"

lsenjov16:08:06

When you call dispatch, the event is placed in the queue instead of immediately being run - helps with the async singlethreaded nature of js. Far less likely to do bad things in the middle of a render cycle

p-himik16:08:35

That particular concern can be easily solved with requestAnimationFrame though.

lsenjov16:08:25

Aye, trying to write the other things down but words are hard at 2am

lsenjov16:08:11

And failing miserably to do so, apparently

lsenjov16:08:09

So a lot of the reasons I’m coming up with are scaling issues

lsenjov16:08:40

Do you need to do them for a small application? Nah, use functions and swap out an app-state atom

lsenjov16:08:03

But as things get larger, trying to deal with it becomes far more difficult

lsenjov16:08:30

Things like decoupling the calling location from the actual implementation

lsenjov16:08:46

And sidefx are a very useful abstraction

lsenjov16:08:18

The http-xhrio library is a fantastic example - you trigger a page load, maybe save something to the db to display something for the user, and also do another fx for tracking

lsenjov16:08:13

These aren’t things that are useful in a small codebase, but it’s a lot easier to keep things separated as you head for that 20KLOC+ codebase

pinkfrog16:08:09

So it seems to me: 1. Async unblocks user actions 2. Decoupling invocation and implementation (good in itself, but using keyword :event-names makes it hard to find the handler, and also hard to refactor had we change the :event-name) 3. pub/sub benefit

lsenjov16:08:53

Pretty much

lsenjov16:08:25

Although if you use namespaced event names it makes it very easy to find where things are coming from, usually

p-himik16:08:31

I'd say the third one is the main one, even though it is rather ambiguous. Async is easy to handle, as I mentioned. Decoupling is mostly superficial with events - you still have to use the right event name, the right arguments, and call that event at the right time (if the event handler depends on time).

p-himik16:08:26

And by default, re-frame queue is only internally async - if you have a bazillion of events, the rendering, along with any user actions, will 100% be blocked. And there are ways around that.

pinkfrog16:08:19

How do you keep track of the event names? While there is a https://day8.github.io/re-frame/App-Structure/ that advises to put event handlers under a dedicated events.cljs file, but I’d prefer a more domain driven model, where I might put the event handlers under account.cljs, sync.cljs, bookmark.cljs, etc. This makes me hard to find out: what event names I’ve defined? Are the being imported on app load or not?

pinkfrog16:08:18

@U24QP2D4J Could you shed some lights on how 20K loc codebase organizes ?

lsenjov16:08:46

So disclaimer before any of this: this is work done by someone self taught and may/not represent best practice

lsenjov16:08:50

With that out of the way

lsenjov16:08:31

There’s a lot of usage of namespaced keywords - almost to the point of abuse sometimes

lsenjov16:08:53

We’re using pathom, not quite going full fulcro yet but stealing a lot of its ideas

lsenjov16:08:20

Each table/object has its own resolves.clj for backend resolving, then resolvers.cljs for fetching/storing

lsenjov16:08:05

Since each fetch really has 2-3 events with it - the fetch itself, the success which marshalls the data into the db, and the failure event (but often the failure is the same event for many)

lsenjov16:08:38

The mutations similarly have their own, mutations.clj for server side, and mutations.cljs for client side

lsenjov16:08:51

I’ve seen some people that like to put all their events into a single file to keep them there, but frankly that gets too large too quickly for my taste

lsenjov16:08:07

Instead, it usually gets broken down by top level key

lsenjov16:08:27

So all of the above are operating under a specific namespaced key for storing state

lsenjov16:08:57

There’s another set of namespaces for dealing with app routing - which are also under the same top level keyword

lsenjov16:08:35

Then other parts of your application which cross multiples of these do so through these singular events. There’s surprisingly few cases where we need to call a ;db that needs to hit multiple top level keywords, but that’s generally solved by factoring out the functions and calling them across

lsenjov16:08:01

…I have no idea if me describing this makes sense, but hopefully it does?

pinkfrog16:08:28

I am trying to understand it. Is your code publicly available for reference?

pinkfrog16:08:50

You mentioned fulcro. Can I find similar example from fulcro?

lsenjov16:08:55

No this is work stuff, can’t show it

lsenjov16:08:59

Not really, I refer to fulcro more in its model of frontend storing objects as reflections of the database ie normalised tables

lsenjov16:08:05

Each of the resolvers & mutations tables resolve around a table or very small group of tables, their mutation and resolution