This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-02-26
Channels
- # announcements (18)
- # aws (17)
- # babashka (19)
- # beginners (141)
- # calva (73)
- # cider (4)
- # clj-kondo (13)
- # cljs-dev (2)
- # clojure (97)
- # clojure-europe (6)
- # clojure-italy (5)
- # clojure-nl (1)
- # clojure-spec (25)
- # clojure-sweden (2)
- # clojure-uk (25)
- # clojured (3)
- # clojurescript (63)
- # core-typed (6)
- # cursive (23)
- # data-science (4)
- # datomic (74)
- # fulcro (19)
- # graalvm (18)
- # graphql (3)
- # hoplon (63)
- # jackdaw (1)
- # juxt (23)
- # london-clojurians (3)
- # meander (7)
- # off-topic (23)
- # om (1)
- # pathom (13)
- # pedestal (2)
- # perun (2)
- # re-frame (38)
- # reagent (3)
- # reitit (24)
- # shadow-cljs (91)
- # spacemacs (14)
- # sql (4)
- # tools-deps (8)
- # vim (3)
Question here on best practices: What’s a good way to organize re-frame effects, events, and subscriptions?
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.
So child namespaces depend on parent namespace subscriptions?
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.
Ah, ok I see.
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
I’ll have to check that out….
So does that mean you use namespaced keywords?
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
Makes sense….
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
@U0524B4UW Just out of interest - what tooling do you use? Cursive can search such aliased keywords just fine.
emacs/cider
So do you have a cljs namespace that is nested, or more flat, in that case? @U0524B4UW
@U054BUGT4 How do you name files with both events and subs in them?
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.
I see. The ‘official’ model suggests separate dirs for each view for a large app, yet Eric N and others differ in their approach.
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.
So you could have a flat structure, where all related code is in the same file, easy to find and manage.
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 .
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.
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.
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?
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.
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.
That makes sense
I’m not at the point of using Datomic yet
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.
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.
While I’m thinking about it, @U054BUGT4 do you use double-colon namespaced keywords? Or just the ‘synthetic’ namespaces, like :foo/bar
?
I use the :foo/bar
style
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.
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.
But sometimes it just results in a long chain of the same args passed down, down down… like turtles, lol.
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 :)
Ah, good point!