Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

"Reagent is the recommended approach for building ClojureScript applications with Luminus."

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



Clarification: Re-frame is not an alternative to reagent, it's a framework or pattern utilizing reagent.


Indeed, you wouldn't (probably) use re-frame unless you're also using reagent (although you could).

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


re-frame still adds way too much boilerplate for my tastes, especially on solo projects. I personally greatly prefer to use reagent directly over using re-frame.


Yes. I now use Rum + Datascript + core.async for event sourcing. Here is the entirety of my reactive event-loop handling, inspired by Tonsky's Cat Chat (https://tonsky.me/blog/datascript-chat/):

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


I use those same 3 in a similar way, it's great. I do really appreciate the design of the effects/coeffects concept in re-frame, I often use that concept. I just don't find I ever need re-frame.


We've had a great experience building a mid-size app with re-frame. Sure, writing subscription and event handlers and wiring it all together seems like a lot of boilerplate when you could just fetch().then(), but in the long run the architecture really pays off.

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 one thing re-frame does well is cascading events. One event might trigger other events, etc. There is also a library for orchestrating re-frame events: https://github.com/day8/re-frame-async-flow-fx

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.


For solo projects that don’t need all the boilerplate I think Hoplon is an even better option (there’s a Luminus profile for it). It’s kinda like Svelte: it doesn’t use a vdom and makes interacting with the actual dom really simple: http://hoplon.io/


Hm, really? Maybe you're missing a layer of indirection if that's so?

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


Reagent is good for starting if you are new to React, but quickly becomes limiting.

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.


Reagent and rum both wrap react right? Do they mirror the react hook logic in some way? E.g rum/react might be a ones to one with reacts use state hook.


Yes, Reagent and Rum are both wrappers around React.

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.


Keechma looks better than re-frame imo https://github.com/keechma/keechma

Wish it had tutorials for newbies like me.


I am the author of the Keechma framework. If you have any questions please ping me on the Clojurians Slack (#keechma channel). I’ll be happy to help


I'm partial to kee-frame[0], which is the controller model from keechma implemented in re-frame. It's a tiny library but quite a nice starting place.

[0]https://github.com/ingesolvoll/kee-frame


Just in case - Luminus has options for adding re-frame and kee-frame (an add-on which adds a lot of convenience stuff to re-frame) to a new project.


I tried using reagent but wasn’t a fan of the atoms everywhere approach. Fine for small projectS IMO.


Do you mean cursors everywhere, or actual atoms? I maintained a fairly large project (3 man years or so) that used local atoms only for ephemeral components where you'd want to lose their state when hidden.

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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: