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