Fork me on GitHub
#re-frame
<
2020-02-26
>
jmckitrick22:02:03

Question here on best practices: What’s a good way to organize re-frame effects, events, and subscriptions?

shaun-mahood22:02:33

Not sure it's a best practice, but here's what I've come to prefer - treat effects as standalone things, such that they can easily be pulled out into a library if desired. - keep events and subscriptions together in a logical namespace for them - in one of my apps I've got the namespaces`page`, page.panel, and page.panel.job - page.panel subscriptions depend on page subscriptions. I tried this based on a recommendation from one of the courses or articles at http://purelyfunctional.tv, and I've found it a lot nicer than the other ways I've tried. Refactoring and finding related events/subscriptions have involved a lot less jumping around to different files.

jmckitrick22:02:06

So child namespaces depend on parent namespace subscriptions?

shaun-mahood22:02:25

Yeah - I've got page.cljs with the subscription :page and all page events, and page.panel.cljs contains the :page/panel subscription and all panel events.

jmckitrick22:02:16

Ah, ok I see.

shaun-mahood22:02:09

One thing I really like about the structure is that it allows me to define computation functions as described in https://github.com/day8/re-frame/blob/master/docs/Testing.md#subscription-handlers and they will have a similar name in both re-frame and when calling directly from Clojure

jmckitrick23:02:57

I’ll have to check that out….

jmckitrick23:02:15

So does that mean you use namespaced keywords?

shaun-mahood23:02:57

Yes, though the namespace in re-frame doesn't have to correspond to the namespace of the clojurescript file, but the way I would refer to it in my data model

jmckitrick23:02:43

Makes sense….

mccraigmccraig06:02:15

we explicitly decided to not use real cljs namespace prefixes, because real namespace prefixes can be aliased, which makes finding all uses of a sub or event harder

p-himik06:02:38

@U0524B4UW Just out of interest - what tooling do you use? Cursive can search such aliased keywords just fine.

jmckitrick22:02:23

So do you have a cljs namespace that is nested, or more flat, in that case? @U0524B4UW

jmckitrick23:02:32

@U054BUGT4 How do you name files with both events and subs in them?

p-himik23:02:51

I'm using an approach similar to Shaun's. I just name files according to what they contain, semantically. login_form for a login form, for example. Not a login_form dir with subs, events, views in it - such arbitrary separation by itself just impedes development. YMMV.

jmckitrick23:02:50

I see. The ‘official’ model suggests separate dirs for each view for a large app, yet Eric N and others differ in their approach.

p-himik23:02:29

I tried that. It doesn't scale. At least, that was my experience. Whenever I need something that must be included in multiple places and cannot be put in e.g. login_form.clj, I just create something like login_form/common.clj or something like that. The naming, again, depends on the context - not on the type of code.

jmckitrick23:02:05

So you could have a flat structure, where all related code is in the same file, easy to find and manage.

shaun-mahood23:02:04

I've tried pretty much every way of separating them, and I found that having everything together in a single login_form file got confusing as well - something about mixing the view and db sections didn't work for me. I now start with everything under core.cljs, then when my views grow too much I move them into something like views/panels.cljs. When that grows too much, it splits into views.panels.panel1. I've never grown my db past a single file, and generally leave it in core.cljs now (all it has in it is the initial state). As my subs and events grow, I separate those based on the logical or data model - page.cljs has all the subs and events related to the page, then when that grows too much it separates into page/panel.cljs, then page/panel/panel-1.cljs. The navigation is a little annoying, but I kind of prefer page.cljs to page/core.cljs since it gives a clearer idea of which files can depend on the others .

shaun-mahood23:02:22

Part of the reason I like to separate my views from the rest is that they seem to evolve differently and may cross boundaries between different logical sections. I sometimes have subscriptions and events within those view files as well if they are specific to only that view, but I've been kind of unhappy with that and am thinking of converting all of my views into pure reagent components so that I can more easily use them with devcards and for testing purposes.

p-himik23:02:23

Ah, yes - I usually keep db specs separate. Although that's just a habit. Now that I think about it, I might just move it in login_form as well to make keyword aliasing a bit simpler. But yeah, if an NS grows too large, I split it as well.

jmckitrick23:02:49

Not to belabor the point, but @U054BUGT4 as you split the code out, I assume you are expanding the file hierarchy to reflect that, so the views dir, then a panels dir, etc, right?

jmckitrick23:02:48

Got it. Ok, I’ll give that a shot. I’m especially interested in trying the ‘signal’ style or or stacked subscriptions (calculations of signals?) which seem to work well in the model you describe.

shaun-mahood23:02:12

What I'm really trying to do is get to where I can make things more consistent across my entire stack - I want to have the same logical model in Datomic, Clojure, and re-frame so that there is less conversion or thinking about where things should go.

jmckitrick23:02:56

That makes sense

jmckitrick23:02:09

I’m not at the point of using Datomic yet

shaun-mahood23:02:53

They're pretty separate things, I'm just in the position where I'm building full stack applications on my own - so I am always trying to optimize for that.

jmckitrick00:02:42

I’m starting a new project from scratch, and I’m not sure how large it will grow. I’m trying to lay the groundwork for easy expansion without overengineering.

jmckitrick23:02:26

While I’m thinking about it, @U054BUGT4 do you use double-colon namespaced keywords? Or just the ‘synthetic’ namespaces, like :foo/bar ?

shaun-mahood23:02:28

I use the :foo/bar style

jmckitrick00:02:09

Now that I think about it, it doesn’t need extra tooling to jump around. That’s a big deal. Maybe save the :: style for specs.

jmckitrick00:02:04

Last question (probably) for today… do you tend to pass data (subscriptions) into components or simply embed the subscription right where it’s needed? I know a lot of ‘reacters’ use ‘smart containers’ that handle the API calls, then pass that data as parameters to ‘dumb components’ that they contain.

jmckitrick00:02:22

But sometimes it just results in a long chain of the same args passed down, down down… like turtles, lol.

shaun-mahood01:02:11

I bounce back and forth - the issue with embedding subscriptions is that it makes testing and reuse more difficult, so you will probably have to experiment a bit and see what you prefer. I’m still trying to figure it out :)

jmckitrick01:02:18

Ah, good point!