Reagent is nice, but if you want global state management, re-frame adds most familiar concepts from redux but without as much boilerplate: https://github.com/day8/re-frame
Reagent is nice, but if you want global state management, re-frame adds most familiar concepts from redux but without as much boilerplate: https://github.com/day8/re-frame
I think vga805 is referring to the "atom pattern" where you have a atom on the outside and/or inside the components that you interact directly with, which is mostly (but not always) replaced by subscriptions and setting up dispatches.
So re-frame would replace the typical state management pattern you use when you're using reagent, but it wouldn't replace the view layer.
Extra clarification I guess...
It's not perfect, I do lament the boilerplateyness myself, but I have not seen anything better in my decade of web dev.
I think using re-frame rather than just building event loops yourself with channels and atoms give you some advantage in terms of existing libraries and utilities.
; One Macro in events.clj:
(defmacro go-loop-sub [pub key binding & body]
(let [[db-bind & bindings] binding]
`(let [ch# (cljs.core.async/chan)]
(cljs.core.async/sub ~pub ~key ch#)
(go-loop []
(let [event# (rest (cljs.core.async/<! ch#))
~db-bind @yourproject.data.core/conn
~(vec bindings) event#
result# (do ~@body)]
(when result#
(yourproject.data.core/transact! yourproject.data.core/conn result#)))))
(recur)))))
;; Usage (at top of core file)
(defonce conn (d/create-conn schema))
(defonce event-bus (async/chan))
(defonce event-pub (async/pub event-bus first))
;; For each event you want to dispatch on:
(go-loop-sub event-pub :todo/add-item [db {:as data :keys [text]}]
;; Query db here. Any tx-data you return will get transacted.
[{:db/id -1
:todo/text text}])
;; To display some reactive queries in DataScript:
(rum/defc root-component
< rum/reactive [conn]
(let [db (rum/react conn)
todos (d/q '[:find ?todo ?text
:where [?todo :todo/text ?text]]
db)]
[:div "Todos: " (pr-str todos)]))
That's it. You don't need re-frame.Usually I end up wrapping things myself so I have one "setup-handler :name" function/macro that creates the subscription and event db handler for me. There are also existing libraries that does this for you, if writing it yourself is not wanted.
One example of such library: https://gitlab.com/nikperic/reframe-utils
`reg-basic-sub` has this outcome:
(reg-basic-sub :common/active-page)
;; Equivalent to
(reg-basic-sub :common/active-page :common/active-page)
;; Equivalent to
(reg-sub
:common/active-page
(fn [db _]
(:common/active-page db)))I found Rum (https://github.com/tonsky/rum) to be a much more flexible choice. It doesn't force you into a single way of doing things, but rather offers a composable way of adding behavior to components using mixins. Especially for larger and more complex applications this proves to be a good tool.
Another thing which I really like about Rum is "isomorphic rendering" (not a good name, but I didn't invent it) — pre-rendering the DOM on the server instead of shipping an empty page to the client and requiring the client to render everything using JavaScript.
I think React hooks arrived much later and from what I've read about them, they solve a problem that does not exist in Clojure/ClojureScript.
`rum/react` lets your components auto-update when an atom changes.
Wish it had tutorials for newbies like me.
Components were passed cursors in to a single global atom, which more or less worked OK.
I think I'd use the re-frame model starting from scratch, but managing the app's state was probably not a top 5 problem for me. Every program is different of course!
Integrating external libraries, validating data, server side rendering, caching remote queries and tracking down compilation/logic errors would probably be my top 5. This was 5 years ago though.