Re-frame is a layer on top of reagent, which is an API for using React in cljs. If you want to use react, you can also just directly use reagent which is the preference of many in the community. The boilerplate and verbosity required for reframe could be overkill for many small projects, especially for solo developers, where pure reagent often thrives. However reframe’s restrictions can be helpful in team environments.
I personally prefer reagent directly over re-frame. I've heard some Clojure developers express frustrations with the typical idioms that Re-frame promotes. I can see some value in how it organizes some things, but you can do the same thing directly with Reagent far more quickly. The difference is that in Re-frame you only really have one way of doing things, and that consistency can be good for a team. But it does require extra development time.
I think the parent is correct to point out two things. That re-frame is useful in bigger teams and it is useful for larger apps.
Re-frame is somewhat more complex than inline and vanilla reagent. I suppose with hooks a lot of the simpler use cases for state management also go away.
That said, as a discipline the indirection re-frame encourages you to use helps you architect a purely functional* application. My observation is a lot of larger user facing applications will tend towards something that is event-driven and possibly loosely a state machine. Which incidentally also is one of the implementations of "functional core imperative shell"
Ultimately YMMV. If you just want a static site with a tiny bit of sparkle then you probably don't need reframe. (possibly you don't need cljs/reagent)
If you're tending towards a large full blown SPA with lots of interactivity and IO plus failure states, then reframe probably isn't a bad solution IMO.
* subscribe and dispatch are strictly not pure and actually use global state so purely functional isn't actually accurate. But it is a reasonable stab at being purely functional, while strongly quarantining the side-effecting bits.
I've run a team with re-frame and a team with reagent (and a convention of a single state atom.) Reagent by itself scaled far better. I use re-frame if it's a company convention, but I'd far rather just ditch it.
Plain reagent has no unnecessary function registry; mutations are done with `(swap! my-cursor foo/update-foo bar)` rather than the extra overhead of `(dispatch [::foo/update-foo bar])`. This also helps new people who rely on cursive to jump to definitions, and don't have a preferred emacs setup.
Reagent testing can be scoped to a single DB. In re-frame, you can clear the subscription queue (which will not handle callbacks from network calls putting events on to the queue when the callback fires, leading to flaky tests that receive unexpected events.) In reagent, you have more power, since you can call `(reagent/atom {})` with your test state and any callbacks specfic to the test will see their DB, regardless of when they resolve.
Re-frame's single events/subscriptions files scaled horribly; once we got beyond a few hundred business-logic-filled events, velocity on changes involving that file slowed down a lot. We broke convention and moved events into namespaces closer to the code.
Yep, pretty large & business critical app. BTW, while I'm not sold on re-frame personally, it is used in a sizable project that has powered a lot of business for us, so thank you for developing it!
Three things that would make re-frame better, if you want them:
- A supported/blessed way to switch out the entire app-db entirely for the purposes of testing
- Use of symbols rather than namespaced keywords for function dispatch
- Docs that give alternate strategies for code organization (fine to have the events.cljs convention, but would be nice to see some viewpoints like having one per child ns similar to angular's code organization model.)
- I'm wondering if re-frame-test might help?
- That ship has sailed a long time ago. And, even if it hadn't, I diagree that symbols would be better.
- I'm unfamiliar with angular's code. The Resources section lists larger apps which you can inspect.
I'll try re-frame-test, thanks! If it doesn't do something like `(with-redefs [re-frame.db/app-db (reagent/atom {})] ...)` it is still open to this bug, though. Because the call to `dispatch` happens inside of a callback the test library has no way to signal that future dispatches coming from a different function context should not be put on the same queue.
It'd be hard to change that aspect of the API now, and I wouldn't recommend you do it; but right now, editor completion and new-user understanding is just better on the reagent side. They do this:
(defn assoc-foo [db bar]
(assoc db :foo bar))
(defn fetch-new-foo [db bar]
(go
(let [bar (<! (get-bar)]
(swap! db assoc-foo bar))))
(swap! db assoc-foo bar)
(fetch-new-foo db bar)
The first one has a lot going for it; swap! is part of the standard library, has a bunch of docs for free, etc.; any clojure editor will pick up on the use of assoc-foo as a function and give you arity warnings if you pass too many parameters, etc.
I've looked at the larger apps - the way I've seen people be most successful if if they have a child namespace with events, subs, & views laid out on a per namespace basis (`my-ns.events, my-ns.subs, my-other-ns.events, my-other-ns.subs`.) Partially due to the tone the docs take, there's always some pushback as to whether that's the re-frame way to do things, and I'd love to just be able to avoid that whole conversation in the future.
Hmm. In my experience, this an unusual opinion (to me, the author). Over the years the re-frame documentation has been the most praised part of the project.
Could you point out the area which you found unnecessarily difficult, and I'll happily review it. Could you also include your background, so I know where you are coming from? The docs are a little oriented towards JS developers coming across, but perhaps you are already very experienced with Clojure and data-oriented design, which makes it seem like the concepts more obvious. Maybe.
Said with love, happy re-frame user: many developers want to be able to match use cases to docs and code examples as quickly and directly as possible. The re-frame docs contain a lot of very verbose motivation and philosophy (over multiple pages) which while useful context, obfuscates the practical content. All of re-frame’s philosophy would be immediately evident from some code examples on the top level readme on GitHub.
This is on point. Whenever I have tried reading the re-frame docs (I actually have tried more than once during the prevt years) I always get frustrated/bored from the lack of actual use cases and explanations of why this could be useful to me!
The docs just seem to academical for me... They would be excellent for a master's thesis but not for somebody deciding if he would like to invest time into a project.
Thanks for the feedback. In this regard, I think things have improved a little over the last 8 months, stepwise.
In our busy world, people want more immediate summaries and progress. These docs arose out of me trying to teach my team how to think about coding in Clojure and to use re-frame (we were all new to the language at the time), so they definitely have a didactic flavour.
I continue to nudge them in the direction you recommend.
I’m another person, but: when I initially read an old version of it (about 4 years ago) I liked the comprehensiveness but disliked the tone. Perhaps due to not being a native english speaker, it distracted me and made it harder to get the point.
Quickly peering it it seems that it has been improved, although some things there remind me of what I felt, such as:
McCoy might report "It's MVC, Jim, but not as we know it". And you would respond "McCoy, you trouble maker, why even mention an OO pattern? re-frame is a functional framework."
This does not help me a lot; although I know about “the real mccoy” expression, I dont understand/enjoy that.
Well, I'm glad to hear it might have improved. After taking a battering in this thread, I'll take any possible small win.
What you reference is an attempt at a joke which requires certain cultural knowledge (Early Star Trek). On this point, I'm probably unlikely to change, and will just have to ask for your forgiveness. I like my docs to have an occasional joke, whether I'm the reader or the writer. But I am aware that this is not everyone's cup of tea (English cultural reference).
The re-frame docs and "marketing" speak, like on its front github page, would do very well to remove a lot of the self-congratulatory tone, frankly. A lot of it reads like someone very impressed with themselves rather than text about how to use an actual tool.
Please don’t listen to the peanut gallery. The re-frame docs are in a league of their own and most of us appreciate the enormous effort put in to make them awesome. Keep rocking.
The docs are great and re-frame is a pleasure to work with. It make take some time to read through the docs but it is a worthwhile endeavor as the concepts build upon each other. They reward those willing to take the time. When I was a newcomer to re-frame and cljs I did not find the re-frame docs to be pretentious or off putting in any way. If not for re-frame and the re-frame docs I'm not certain I would have continued to use Clojure or cljs. Thank you for building this excellent tool and providing high quality documentation to back it.
I really like the re-frame docs, thank you very much for putting so much effort into them. They've helped me grow as a programmer. For context, I first stumbled across the readme in ~2015-16 in college before working professionally. As someone looking to learn I enjoyed the didactic bent, I felt like I got the big picture. It tied together a lot of Clojure kool-aid.
The vibe I get from the tone of the docs isn't really juvenile or self-congratulatory. The humor is mature, references made show that material therein have been considered. It's not memes and emojis and WOW! It's somewhat unprofessional, has a few in-jokes, but I like those signals. If the docs haven't succumbed to design-by-committee lowest-common-denominator then neither has the code. It gives me the feeling that someone took the slow road, thought about a lot of things, came out with something simple, and is likely to work hard to preserve that simplicity.
The docs tell me that re-frame is focused on the craft. That means a lot more to me than sprinting to see how do I do x in y quickly.
Well I tried to write a balanced comment and try not to hurt but it seems I failed. As I said, the content was good; and I did not say it but I would kill for that level of docs in every open source project. So sorry if I hit your emotions and thanks for your work!!
My background: I have several years of Clojure(script) experience, and used various other cljs frameworks before trying re-frame, so my view is probably biased.
And yet I've also had people tell me the opposite - they say they liked the water cycle analogy, even more than the subsequent dominoes narrative. Hmm. Hard to know what to do about such conflicting feedback.
If you try to come up with lots of clever analogies and complex stories, layered with lots of self-congratulatory enthusiasm, some people will get it or like it, many will not.
On the other hand, if you write simply and just get to the point, everyone will benefit. Leave out the editorials and just say what needs to be said so developers can use their time on their code.
You could easily remove 80% of the words in all documentation about re-frame without losing the important content.
It’s only because I have a lot of respect for the Clojure language and ecosystem as a whole, but I often see non-Clojure developers or only hobbyists refer to the community as pretentious and get put off by that, so I like to call out those things that are unfortunately adding to that impression. Sorry if I overdid it.
Very happy re-frame user here - I was pleased to see the 1.0 release recently and I like the sound of the new developments.
Personally I do appreciate the amount of conceptual framing (pun intended!) contained in the re-frame docs. It's one thing to know the syntax for, say, declaring a subscription, but it's something else to know what subscriptions are for. For me, Re-frame's value-add is that it provides a bunch of sensible patterns for structuring an app, so this stuff clearly matters more than it would for other libraries.
That said, I think some of the basic elements of how to use re-frame are harder to find than they should be. There's the instant gratification "just show me some code so I know what I'm dealing with here" problem, where I think people just want to see a bit of sample code in order to orient themselves.
Re-frame has a very positive story here, because the API is very concise: you could show registering an event and a subscription, and subscribing and dispatch in the view layer in maybe 15 lines of code. For a new developer, the understanding that the API is mostly just two functions and two macros makes the whole thing look much less intimidating.
The reference docs are also a bit tricky to work with. The generated docs tell you about functions that you will probably never call, and crucially the generated docs don't include built-in effects. I'm sure there is some documentation on how to use `dispatch-n` somewhere, but I'm damned if I can find it and so I normally just read the source code to remind myself.
I'm a react developer and I immediately wanted to see code examples. I looked through the docs and couldn't easily find any. I went to the API docs on mobile and was met with layout issues (iphone 11 on Chrome).
If those code examples were in an unfamiliar language (ClojureScript), would they still be useful? I've been under the impression that the concepts would have been a more helpful in that case.
It can't be too unusual; I've seen this critique shared in forums, to which you've replied, for years.
I've seen developers remark how turned off they got by reading stuff like this on its front page, which is frankly just noise and undermines the goals of a serious project:
> re-frame is lucky enough to enjoy an unfair advantage...When we use Lisp, we get to leverage 50 years of foliated excellence from the very best minds available.
I mean, come on. The very best minds work in many different fields with many different languages. Lisp doesn't own the best minds, and neither does re-frame. The fact that lisp or Fortran or other languages have been around for many decades is kinda irrelevant, and this writing style permeates all the docs.
> Travel the geodesic.
> an immaculate hammock conception
The heart of the how-tos is often hidden in lengthy prose that celebrates itself.
> I've seen developers remark how turned off they got by reading stuff like this on its front page, which is frankly just noise and undermines the goals of a serious project:
This was exactly my experience. Professional clojurescript developer using re-frame, but not typically one to chat on forums. I wouldn't have shared this opinion until prompted to by this forum, but it's an opinion I've held privately for longer.
> It can't be too unusual; I've seen this critique shared in forums, to which you've replied, for years.
In this thread, I was responding to someone saying the concepts were simple but explained with too many buzzwords. I certainly have no recollection of someone of someone saying that before.
On the other hand, I have heard some say they'd want more code examples earlier. And, just as soon as I get time I'll be doing that. Unless you want to supply a PR yourself - given your posting frequency you seem very, very invested.
I would love to try out all these cool alternatives to pure JS plus Vue or React, but at what point is this a good idea for a commercial production application?
At this point I think it's safe to say---clojurescript has proven itself and is not a risk.
An excellent language, good libraries (Metosin's ambidextrous router, Reitit, stands out here), the best development story with Figwheel, and oh, dead code elimination for free, as the cljs compiler leverages Google's Closure (not to be mistaken for Clojure) compiler.
Not to mention that cljs is actually a better fit for React even than JavaScript---implicit return, bias towards functional programming, and it requires no magic JSX step---in cljs you just write your html in nested data literals. It's a dream.
I haven't even mentioned black magic like core.async, or that now your front-end devs have a foothold on the JVM rather than the more-impoverished Node ecosystem(oh, you want to keep Node? Well, cljs runs there too!).
Pitch is a well-funded German startup making a presentation tool in the browser---as in, they can't afford to get their front end wrong---and they're comfortably using cljs.
Frankly at this point I think anyone serious about the frontend, who wants it to be good, not just passable, should be using clojurescript. There are reasons not to---if you need something today, and don't have time to learn a lisp---but for anything not due next week, I'd recommend cljs.
Conversely, I found the experience pretty terrible when I worked on a big cljs/reagent/reframe/figwheel app over the course of a couple of years. It was slow, prone to runtime errors (though not as much as JavaScript), and often presented weird edge-cases at the intersection between the Lisp world and the React world. The company ended up abandoning all this stuff after having been big Clojure/ClojureScript evangelists for a long time.
On the other hand, after having built several large Elm applications, Elm is still the tool I'd recommend. On my current ~10KLOC Elm project, compilation is near enough instant, the structure of the code is more rigid so it's far easier to understand — even for beginners — and there's far less potential for runtime errors to occur.
Of course, this is the classic religious war in programming, so I expect someone to appear soon and point out that there is no empirical evidence to support the idea that a type system like the one in Elm or Haskell results in fewer errors in practice. Or
perhaps they'll say jet fuel can't melt steel beams.
Hmm. This is likely to devolve into a static vs dynamic typing borefest. There is no right here: it is just different brain wiring (preferences), backed by motivated reasoning on both sides.
But to address the three points you raise...
I'm surprised to hear that there was a problem at the Reagent/React boundary. We find that works sooooo well.
I genuinely love Elm's claims around 0 runtime errors. It gets my nerdy juices flowing. But then I remind myself that this is simply not a problem I have. We might get one every blue moon (in production) and then it is fixed and deployed about 10 mins later. I have many technical challenges (eg. Kubernetes etc), but runtime problems is not in my top 20.
As to slowness, I can't comment without knowing more. We don't have that problem but that doesn't mean it might not exist in some domains. Then again, it could be bad design decisions. Dunno.
One thing for sure, I'd choose Elm over javascript any day, but ClojureScript is just working too well for us right now.
I'm glad you commented, because the GP comment originally had at the end something like, "With one caveat---I still haven't given Elm a fair shake." Then I thought, "Nah, too esoteric for HN, I don't want to dilute things" and erased it. Joke is on me! I do hope to be able to make the time to play with Elm at some point---at one point I was very excited about FRP, and what took me away was time, not some fundamental, principled thing.
Does the bundle size matter? CLJS bundles are not terribly huge but if you need a very lightweight bundle you're out of luck. Vs plain JS there is just no comparison. Vs plain JS + just a few libraries CLJS will probably do worse too. But for anything bigger it starts to catch up. Say I'd never use CLJS for a landing page, but for most kinds of apps there shouldn't be a problem.
How do you plan to interact with the wider JS ecosystem? One problem with CLJS is while you can pull in most JS stuff, it doesn't work the other way around. Your CLJS library will not be a first class citizen in the JS world. It can be done but it's not a good idea. You can contribute to the CLJS world, but forget about contributing anything significant back to the JS world.
If these two don't bother you, you should be fine.
Any CLJS code has to bundle the CLJS runtime. CLJS doesn't compile to idiomatic JS, for example a vector will be a cljs.core/PersistentVector not a JS Array. So code for most CLJS basics has to be bundled with any CLJS code to run.
Let's say you write a tiny library that solves just one problem. I would imagine most people wouldn't want to have to pull in the CLJS runtime for just that.
So let me be clear - there is no problem in using JS libraries, what you linked speaks to using npm libraries which previously was tougher. CLJS is doing very well on that front.
What I'm saying is - if you write a library in CLJS, don't expect plain JavaScripters to use it.
Re-frame is absolutely fantastic in my experience. Here's a beginner workshop on Reagent/re-frame that I ran for Js devs a little while back. If you're interested to try ClojureScript, this will run your through the basics from ground up
I enjoy working with re-frame and can't imagine building single page apps in ClojureScript without it. For some reason the docs are controversial, which is a surprise to me. I appreciate the conceptual framing in the docs and how each section builds upon the previous. I don't mind the references to star trek or statements on the greatness of Lisp.
A common complaint seems to be the docs are too verbose and people just want to jump in and use the library. If this is you, I recommend using the Luminus framework https://luminusweb.com/ to generate a new project template so you can hack on a ClojureScript project with re-frame to see if you like it.
# Make a new project with re-frame
lein new luminus my-project +re-frame
# Start the clj and cljs repls
lein repl
lein figwheel
I’ve looked at re-frame for a couple of years now, since right when I started learning Clojure on my own time, and I’m quite interested but still on the fence about whether it would be helpful for my use case.
Our API calls are messy (historical limitations) and clojurescript’s robust data merging capabilities would work wonders here. In a new front-end iteration I’m trying to use the model of a single global data store, but given how many conditional API calls we have to make and how much easier and more idiomatic it is to “cheat” with mutability in JS, it’s hard to convince my team to be rigorous about keeping to this pattern for everything. There are real difficulties and tradeoffs there: ES6 spreads more than one level deep are hard to read, we may want to display a view before its drop down entries are resolved, etc.
The essentials are that we have to handle retrieving the following:
1. A model that represents some global concerns (auth, network/system errors, websocket messages) plus the data required by the current view.
2. A template that determines how to display the current view. This can be a full-page display with nav tree links to other views plus the display form that a given field should use (input, drop down, button, etc.), or it can be a modal view, n layers deep, and also may or may not contain a nested nav tree of its own. (Yep, we have to keep the lower-layer models ourselves and still merge errors/websocket stuff.)
I’m already confident that cljs can turn the messy model reconciliation into something easier to read and maintain. I’m less certain about re-frame’s idea of how to subscribe to its db data, especially since I’d like to reuse some custom components in deeply-nested data. For example, “sticking a drop down inside of a grid column“ is an expected feature in the view template.
Transforming it from js react to cljs reagent seems like a pretty straightforward exercise—famous last words. The impression I get from re-frame is that it’s “a more robust redux with less boilerplate,” and the dev tools look great. But any proof-of-concept app I develop will be a spare-time if not my-own-time exploration. It’s something I want to do, because I think it’ll save us loads of time later and just be more fun to work with. It’s also an uphill battle to sell using Clojure at my org anyway, and I want to make a good pitch of it without spending too much time in the weeds.
Does anyone have experience with a system like this in re-frame, or in cljs more generally, who is willing to give me some feedback?
56 comments
[ 3.0 ms ] story [ 107 ms ] threadThe library github page is an entertaining display of equal parts vanity and hyperbole: https://github.com/day8/re-frame/
https://day8.github.io/re-frame/FAQs/DoINeedReFrame/
Re-frame is somewhat more complex than inline and vanilla reagent. I suppose with hooks a lot of the simpler use cases for state management also go away.
That said, as a discipline the indirection re-frame encourages you to use helps you architect a purely functional* application. My observation is a lot of larger user facing applications will tend towards something that is event-driven and possibly loosely a state machine. Which incidentally also is one of the implementations of "functional core imperative shell"
Ultimately YMMV. If you just want a static site with a tiny bit of sparkle then you probably don't need reframe. (possibly you don't need cljs/reagent)
If you're tending towards a large full blown SPA with lots of interactivity and IO plus failure states, then reframe probably isn't a bad solution IMO.
* subscribe and dispatch are strictly not pure and actually use global state so purely functional isn't actually accurate. But it is a reasonable stab at being purely functional, while strongly quarantining the side-effecting bits.
Plain reagent has no unnecessary function registry; mutations are done with `(swap! my-cursor foo/update-foo bar)` rather than the extra overhead of `(dispatch [::foo/update-foo bar])`. This also helps new people who rely on cursive to jump to definitions, and don't have a preferred emacs setup.
Reagent testing can be scoped to a single DB. In re-frame, you can clear the subscription queue (which will not handle callbacks from network calls putting events on to the queue when the callback fires, leading to flaky tests that receive unexpected events.) In reagent, you have more power, since you can call `(reagent/atom {})` with your test state and any callbacks specfic to the test will see their DB, regardless of when they resolve.
Re-frame's single events/subscriptions files scaled horribly; once we got beyond a few hundred business-logic-filled events, velocity on changes involving that file slowed down a lot. We broke convention and moved events into namespaces closer to the code.
- A supported/blessed way to switch out the entire app-db entirely for the purposes of testing
- Use of symbols rather than namespaced keywords for function dispatch
- Docs that give alternate strategies for code organization (fine to have the events.cljs convention, but would be nice to see some viewpoints like having one per child ns similar to angular's code organization model.)
It'd be hard to change that aspect of the API now, and I wouldn't recommend you do it; but right now, editor completion and new-user understanding is just better on the reagent side. They do this:
whereas re-frame would make you do this: The first one has a lot going for it; swap! is part of the standard library, has a bunch of docs for free, etc.; any clojure editor will pick up on the use of assoc-foo as a function and give you arity warnings if you pass too many parameters, etc.I've looked at the larger apps - the way I've seen people be most successful if if they have a child namespace with events, subs, & views laid out on a per namespace basis (`my-ns.events, my-ns.subs, my-other-ns.events, my-other-ns.subs`.) Partially due to the tone the docs take, there's always some pushback as to whether that's the re-frame way to do things, and I'd love to just be able to avoid that whole conversation in the future.
Could you point out the area which you found unnecessarily difficult, and I'll happily review it. Could you also include your background, so I know where you are coming from? The docs are a little oriented towards JS developers coming across, but perhaps you are already very experienced with Clojure and data-oriented design, which makes it seem like the concepts more obvious. Maybe.
The docs just seem to academical for me... They would be excellent for a master's thesis but not for somebody deciding if he would like to invest time into a project.
In our busy world, people want more immediate summaries and progress. These docs arose out of me trying to teach my team how to think about coding in Clojure and to use re-frame (we were all new to the language at the time), so they definitely have a didactic flavour.
I continue to nudge them in the direction you recommend.
Quickly peering it it seems that it has been improved, although some things there remind me of what I felt, such as:
This does not help me a lot; although I know about “the real mccoy” expression, I dont understand/enjoy that.What you reference is an attempt at a joke which requires certain cultural knowledge (Early Star Trek). On this point, I'm probably unlikely to change, and will just have to ask for your forgiveness. I like my docs to have an occasional joke, whether I'm the reader or the writer. But I am aware that this is not everyone's cup of tea (English cultural reference).
The vibe I get from the tone of the docs isn't really juvenile or self-congratulatory. The humor is mature, references made show that material therein have been considered. It's not memes and emojis and WOW! It's somewhat unprofessional, has a few in-jokes, but I like those signals. If the docs haven't succumbed to design-by-committee lowest-common-denominator then neither has the code. It gives me the feeling that someone took the slow road, thought about a lot of things, came out with something simple, and is likely to work hard to preserve that simplicity.
The docs tell me that re-frame is focused on the craft. That means a lot more to me than sprinting to see how do I do x in y quickly.
My background: I have several years of Clojure(script) experience, and used various other cljs frameworks before trying re-frame, so my view is probably biased.
On the other hand, if you write simply and just get to the point, everyone will benefit. Leave out the editorials and just say what needs to be said so developers can use their time on their code.
You could easily remove 80% of the words in all documentation about re-frame without losing the important content.
Personally I do appreciate the amount of conceptual framing (pun intended!) contained in the re-frame docs. It's one thing to know the syntax for, say, declaring a subscription, but it's something else to know what subscriptions are for. For me, Re-frame's value-add is that it provides a bunch of sensible patterns for structuring an app, so this stuff clearly matters more than it would for other libraries.
That said, I think some of the basic elements of how to use re-frame are harder to find than they should be. There's the instant gratification "just show me some code so I know what I'm dealing with here" problem, where I think people just want to see a bit of sample code in order to orient themselves.
Re-frame has a very positive story here, because the API is very concise: you could show registering an event and a subscription, and subscribing and dispatch in the view layer in maybe 15 lines of code. For a new developer, the understanding that the API is mostly just two functions and two macros makes the whole thing look much less intimidating.
The reference docs are also a bit tricky to work with. The generated docs tell you about functions that you will probably never call, and crucially the generated docs don't include built-in effects. I'm sure there is some documentation on how to use `dispatch-n` somewhere, but I'm damned if I can find it and so I normally just read the source code to remind myself.
http://day8.github.io/re-frame/api/
Then click "Builtin effects" in the left Nav.
I'm actually working on the API docs at the moment and wrestling with codox and markdown interaction.
It can't be too unusual; I've seen this critique shared in forums, to which you've replied, for years.
I've seen developers remark how turned off they got by reading stuff like this on its front page, which is frankly just noise and undermines the goals of a serious project:
> re-frame is lucky enough to enjoy an unfair advantage...When we use Lisp, we get to leverage 50 years of foliated excellence from the very best minds available.
I mean, come on. The very best minds work in many different fields with many different languages. Lisp doesn't own the best minds, and neither does re-frame. The fact that lisp or Fortran or other languages have been around for many decades is kinda irrelevant, and this writing style permeates all the docs.
> Travel the geodesic.
> an immaculate hammock conception
The heart of the how-tos is often hidden in lengthy prose that celebrates itself.
This was exactly my experience. Professional clojurescript developer using re-frame, but not typically one to chat on forums. I wouldn't have shared this opinion until prompted to by this forum, but it's an opinion I've held privately for longer.
In this thread, I was responding to someone saying the concepts were simple but explained with too many buzzwords. I certainly have no recollection of someone of someone saying that before.
On the other hand, I have heard some say they'd want more code examples earlier. And, just as soon as I get time I'll be doing that. Unless you want to supply a PR yourself - given your posting frequency you seem very, very invested.
An excellent language, good libraries (Metosin's ambidextrous router, Reitit, stands out here), the best development story with Figwheel, and oh, dead code elimination for free, as the cljs compiler leverages Google's Closure (not to be mistaken for Clojure) compiler.
Not to mention that cljs is actually a better fit for React even than JavaScript---implicit return, bias towards functional programming, and it requires no magic JSX step---in cljs you just write your html in nested data literals. It's a dream.
I haven't even mentioned black magic like core.async, or that now your front-end devs have a foothold on the JVM rather than the more-impoverished Node ecosystem(oh, you want to keep Node? Well, cljs runs there too!).
Pitch is a well-funded German startup making a presentation tool in the browser---as in, they can't afford to get their front end wrong---and they're comfortably using cljs.
Frankly at this point I think anyone serious about the frontend, who wants it to be good, not just passable, should be using clojurescript. There are reasons not to---if you need something today, and don't have time to learn a lisp---but for anything not due next week, I'd recommend cljs.
On the other hand, after having built several large Elm applications, Elm is still the tool I'd recommend. On my current ~10KLOC Elm project, compilation is near enough instant, the structure of the code is more rigid so it's far easier to understand — even for beginners — and there's far less potential for runtime errors to occur.
Of course, this is the classic religious war in programming, so I expect someone to appear soon and point out that there is no empirical evidence to support the idea that a type system like the one in Elm or Haskell results in fewer errors in practice. Or perhaps they'll say jet fuel can't melt steel beams.
But to address the three points you raise...
I'm surprised to hear that there was a problem at the Reagent/React boundary. We find that works sooooo well.
I genuinely love Elm's claims around 0 runtime errors. It gets my nerdy juices flowing. But then I remind myself that this is simply not a problem I have. We might get one every blue moon (in production) and then it is fixed and deployed about 10 mins later. I have many technical challenges (eg. Kubernetes etc), but runtime problems is not in my top 20.
As to slowness, I can't comment without knowing more. We don't have that problem but that doesn't mean it might not exist in some domains. Then again, it could be bad design decisions. Dunno.
One thing for sure, I'd choose Elm over javascript any day, but ClojureScript is just working too well for us right now.
Does the bundle size matter? CLJS bundles are not terribly huge but if you need a very lightweight bundle you're out of luck. Vs plain JS there is just no comparison. Vs plain JS + just a few libraries CLJS will probably do worse too. But for anything bigger it starts to catch up. Say I'd never use CLJS for a landing page, but for most kinds of apps there shouldn't be a problem.
How do you plan to interact with the wider JS ecosystem? One problem with CLJS is while you can pull in most JS stuff, it doesn't work the other way around. Your CLJS library will not be a first class citizen in the JS world. It can be done but it's not a good idea. You can contribute to the CLJS world, but forget about contributing anything significant back to the JS world.
If these two don't bother you, you should be fine.
Why is this? What makes it a bad idea? Does ClojureScript’s new ‘bundle’ compilation target [0] help resolve whatever issues you are referring to?
Genuinely curious.
[0] https://clojurescript.org/news/2020-04-24-bundle-target
Let's say you write a tiny library that solves just one problem. I would imagine most people wouldn't want to have to pull in the CLJS runtime for just that.
So let me be clear - there is no problem in using JS libraries, what you linked speaks to using npm libraries which previously was tougher. CLJS is doing very well on that front.
What I'm saying is - if you write a library in CLJS, don't expect plain JavaScripters to use it.
https://github.com/ClojureTO/JS-Workshop/tree/shadow-cljs
A common complaint seems to be the docs are too verbose and people just want to jump in and use the library. If this is you, I recommend using the Luminus framework https://luminusweb.com/ to generate a new project template so you can hack on a ClojureScript project with re-frame to see if you like it.
Our API calls are messy (historical limitations) and clojurescript’s robust data merging capabilities would work wonders here. In a new front-end iteration I’m trying to use the model of a single global data store, but given how many conditional API calls we have to make and how much easier and more idiomatic it is to “cheat” with mutability in JS, it’s hard to convince my team to be rigorous about keeping to this pattern for everything. There are real difficulties and tradeoffs there: ES6 spreads more than one level deep are hard to read, we may want to display a view before its drop down entries are resolved, etc.
The essentials are that we have to handle retrieving the following:
1. A model that represents some global concerns (auth, network/system errors, websocket messages) plus the data required by the current view.
2. A template that determines how to display the current view. This can be a full-page display with nav tree links to other views plus the display form that a given field should use (input, drop down, button, etc.), or it can be a modal view, n layers deep, and also may or may not contain a nested nav tree of its own. (Yep, we have to keep the lower-layer models ourselves and still merge errors/websocket stuff.)
I’m already confident that cljs can turn the messy model reconciliation into something easier to read and maintain. I’m less certain about re-frame’s idea of how to subscribe to its db data, especially since I’d like to reuse some custom components in deeply-nested data. For example, “sticking a drop down inside of a grid column“ is an expected feature in the view template.
Transforming it from js react to cljs reagent seems like a pretty straightforward exercise—famous last words. The impression I get from re-frame is that it’s “a more robust redux with less boilerplate,” and the dev tools look great. But any proof-of-concept app I develop will be a spare-time if not my-own-time exploration. It’s something I want to do, because I think it’ll save us loads of time later and just be more fun to work with. It’s also an uphill battle to sell using Clojure at my org anyway, and I want to make a good pitch of it without spending too much time in the weeds.
Does anyone have experience with a system like this in re-frame, or in cljs more generally, who is willing to give me some feedback?