
Re-Frame: Build web apps in ClojureScript and React - tosh
http://day8.github.io/re-frame/
======
hellofunk
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.

The library github page is an entertaining display of equal parts vanity and
hyperbole: [https://github.com/day8/re-frame/](https://github.com/day8/re-
frame/)

~~~
synthc
Re-frame really doesn't add much in addition to Reagent, I really don't
understand its popularity.

~~~
pinchhit
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.

~~~
pinchhit
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.)

~~~
MikeOfAu
Answers to your three points:

    
    
      - 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.

~~~
pinchhit
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)
    

whereas re-frame would make you do this:

    
    
        (re-frame/reg-event-db
          ::assoc-foo
          (fn [db [_ bar]]
            (assoc db :foo bar)))
        
        (re-frame/reg-fx
          ::pull-from-channel
          (fn [{:keys [f event]}]
            (go
              (let [resp (<! (f))]
                (re-frame/dispatch (conj event resp))))))
        
        (re-frame/reg-event-fx
          ::fetch-new-foo
          (fn [_ _]
            {:pull-from-channel {:f get-bar
                                 :event [::assoc-foo]}}))
        
        (dispatch [::assoc-foo bar])
        (dispatch [::fetch-new-foo])
    

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.

------
yogthos
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

[https://github.com/ClojureTO/JS-Workshop/tree/shadow-
cljs](https://github.com/ClojureTO/JS-Workshop/tree/shadow-cljs)

------
frompdx
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/](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

------
synthc
I really dislike the documentation of re-frame. The concepts are quite simple,
but the docs explain them in a really confusing and buzzword heavy way.

~~~
MikeOfAu
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.

~~~
hellofunk
> In my experience, this an unusual opinion

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.

~~~
MikeOfAu
> 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.

~~~
hellofunk
My apologies again for the negative tone of my comment. I've had a bad day and
I let it bleed into my remarks here.

------
dennisy
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?

~~~
invalidOrTaken
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.

~~~
yakshaving_jgt
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.

~~~
MikeOfAu
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.

------
filoeleven
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?

------
b4ke
the documentation seems thirsty for validity, if i am being honest about it's
attempts to impart understanding with it's quip context.

