(newbie) Om/reagent (and react) clarity questions

438 views
Skip to first unread message

Colin Yates

unread,
Oct 23, 2014, 9:04:31 AM10/23/14
to clojur...@googlegroups.com
(apologies if I have overlooked any of this in the docs, it isn't from lack of reading, more reaching saturation point - RTFM is a perfectly good response if it contains a link to the relevant bit :))

My use case is that I have a non-trivial single page app. Inside this app there are a number of distinct areas, for a completely made up domain of car rental:
- searching for/editing/adding a new customer
- searching for/editing/adding a car
- assigning a car to a customer
- receiving a car from a customer
- removing a car due to maintenance/crash
- various reports - top 10 customers, top 10 cars etc.
- and so on

Each functional area is pretty unrelated from the others. Inside each functional area there are individual components that all need to talk to each other.

Is it true that om really wants to manage the entire application state in a single atom. So we might have an atom map structured with keys referencing each functional area {:car-search {} :patient-search {} ...}? I understand that this isn't inefficient as components receive a cursor into their bit of the map thus avoiding unnecessary false changes.

The main app will have an expandable left panel containing the global menu. In dom-manipulation world I would add a "collapsed" or "expanded" CSS class which defined the appropriate widths etc. In om (or rather react) land this is still possible I think, but is it more idiomatic to store the expanded/collapsed flag in the application state thus causing the "panel" component to re-render, the panel component then switching on that "expanded?" flag? The "central" panel also needs to be resized in response to the expansion/collapse, thus both components need to be in-sync. How is this idiomatically handled?

In the more general case, there are components that need to be shown/hidden (tabs, validation pop-up errors etc.). In dom-manipulation world I would set css classes to change style's visibility for example, is this idiomatically done through flags in the application state?

I am stumped as to how routing navigation fits into something like om. Again, is it a case that the navigation handlers simply update the application state? (You can see a theme in my thinking here!)

In terms of reagent is it true to say that it is a bit less opinionated about these things and where-as om has a very opinionated approach to front-end state management (happening to use om), reagent is a (very nice) wrapper to om? Not to trivialize reagent, but is is "simply" trying to introduce clojurescript to react?

Is it also true to say that whilst om wants to manage the whole application, reagent allows you to think about disconnected bits of your app?

FWIW - reagent appeals to my pragmatic "need to get stuff done" and it feels very un-opinionated and very lightweight. However, the more I read about om the more it jives with me. However, I am in the pattern of "yeah, that is how I would solve that problem", I just can't quite connect the dots in the bigger picture.

It is also worth saying that there are no losers here, I am sure I will be delighted using either om or reagent.

I think that is sufficient for now - thanks for reading, and thanks even more for responding :).

Peter Jaros

unread,
Oct 23, 2014, 9:32:22 AM10/23/14
to clojur...@googlegroups.com

A few thoughts, though I’m sure others will have more:

  • Om requires your entire React render tree’s data to be stored in a single atom, but you can have as many render trees as you’d like on a page, each with its own atom of state. It’s more common to just use one, but more than one is an option, if it makes sense for you.

  • In Om, state like showing and hiding a tab is sometimes stored in the application state, and sometimes stored in the component’s local state. That’s a matter of preference. As far as I know there’s no big rule of thumb for when one is better than the other; they each have some subtle advantages and disadvantages.

  • Reagent is more than just a thin veneer over React. It does quite a bit of work, just as Om does. This is subjective, but Reagent feels more easy than simple to me. Sometimes, that’s what you want; sometimes it’s not. But: I’ve barely touched Reagent myself, so take that with a large grain of salt. I’d love to hear a response from someone actually working with Reagent.

  • There is a React wrapper that’s mostly a thin veneer: Quiescent. Quiescent, to me, is at the simple end of the simple-easy spectrum. Unlike Om or Reagent, it doesn’t manage your rendering for you; you store your state where you will and tell it to render the tree when you think it should (which is how React itself works).

I have a blog post that goes into some more detail with links to examples, if you’re curious.

Hooking up URL routing to any of these has had my head spinning as well. I’d love to hear what other people are doing for that too.

Peter



--
Note that posts from new members are moderated - please be patient with your first post.
---
You received this message because you are subscribed to the Google Groups "ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojurescrip...@googlegroups.com.
To post to this group, send email to clojur...@googlegroups.com.
Visit this group at http://groups.google.com/group/clojurescript.



--
Visuals: http://youtube.com/peeja
Words: http://blog.peeja.com/
Conversation: (603) 548-1203

Linus Ericsson

unread,
Oct 23, 2014, 10:09:23 AM10/23/14
to clojur...@googlegroups.com
One cool thing which solves one of the problems (namely get "views" out of the data) is DataScript [1]. There are some nice and quite compact examples [2] which are not that trivial (at least they have two different "widgets").

DataScript is encouraging in that it feels doable to get data out of the model without having to manually make clever but brittle structural assumptions on the inevitable evolving of the data schema.
/Linus

Ahmad Hammad

unread,
Oct 23, 2014, 1:29:30 PM10/23/14
to clojur...@googlegroups.com
re routing with om, it stumped me at first too, i've outlined an evolving approach here https://groups.google.com/forum/#!topic/clojurescript/E2Lxody9SlM would be happy to bounce ideas of any alternative approaches.

Mike Haney

unread,
Oct 23, 2014, 10:59:32 PM10/23/14
to clojur...@googlegroups.com
> Reagent is more than just a thin veneer over React. It does quite a bit of work, just as Om does. This is subjective, but Reagent feels more easy than simple to me. Sometimes, that’s what you want; sometimes it’s not. But: I’ve barely touched Reagent myself, so take that with a large grain of salt. I’d love to hear a response from someone actually working with Reagent.

OK, I'll bite. I'm bothered by your use of the term "easy". As I'm sure you know, that has a negative connotation in our community, and your remarks seem to imply that Reagent is a toy not to be taken seriously for non-trivial apps. That is certainly not the case.

To me, Reagent feels simple, as in just the right level of abstraction, whereas Om feels like incidental complexity. This is just my opinion, but I think perhaps much of the initial popularity of Om was due to the rock-star status of its creator (no disrespect intended - David Nolen IS a rock star in this community, and has made tremendous contributions to our community, and I am very grateful for that). But many people, including myself, seem to be finding the additional complexity unwarrranted in some (most) cases and switched to Reagent.

They are BOTH excellent libraries, and it really comes down to which one fits your app and/or your team's mental model best. From my experience, Reagent is generally easier to work with, and you have a lot of flexibility with structuring your app state, which could be good or bad depending on what you are looking for. Om is much more prescriptive, which can be a good thing too - if you have no idea how you should structure your app, well Om might be a good choice because it does force you into doing things a certain way.

One thing I did early on when trying to decide between Om and Reagent was write a small but non-trivial part of my app in both. I used Kioo for templating, so I didn't have to write the view code twice, plus having all the markup in templates let me really focus on how I was managing state in both versions. I found it very useful, and I think it just makes sense to do a small spike like this before committing to a particular library for a large project.

One last thing - Dmitri Sotnikov has written some nice blog posts on Reagent, which are really helpful for getting starting. I recommend starting with this one: http://yogthos.net/#/blog/54

There is also one on how to do routing with Reagent: http://yogthos.net/#/blog/55


Mike Haney

unread,
Oct 24, 2014, 12:20:25 AM10/24/14
to clojur...@googlegroups.com
> Is it true that om really wants to manage the entire application state in a single atom. So we might have an atom map structured with keys referencing each functional area {:car-search {} :patient-search {} ...}? I understand that this isn't inefficient as components receive a cursor into their bit of the map thus avoiding unnecessary false changes.

Technically, each root component can only have 1 state atom, so you could in theory use multiple roots. I think this ends up being pretty rare in practice, though. For various reasons, most people seem to end up using a single root component for the whole app.



> The main app will have an expandable left panel containing the global menu. In dom-manipulation world I would add a "collapsed" or "expanded" CSS class which defined the appropriate widths etc. In om (or rather react) land this is still possible I think, but is it more idiomatic to store the expanded/collapsed flag in the application state thus causing the "panel" component to re-render, the panel component then switching on that "expanded?" flag? The "central" panel also needs to be resized in response to the expansion/collapse, thus both components need to be in-sync. How is this idiomatically handled?

I have that scenario in my app, and yes I just programmatically toggle those css classes based on the state so I can take advantage of CSS animations (which are hardware-accelerated on mobile platforms).

Here's a gist using Reagent: https://gist.github.com/mdhaney/e873611160341da79d77

These are just a few components for the menu, to show the pattern. There are actually 2 menus, the off-canvas menu to the left, and from there a sub-menu can be selected (I'm using Foundation for my base CSS framework, and haven't completed my conversion to semantic classes, which is why the components are still littered with presentational classes).

A few notes about how I structured this:
1. Most components receive 2 parameters, "state" and "event". The "state" is a map containing all the application state, but not in the Om sense. Most of the values inside state are separate Reagent atoms, I just bundle them up to make the easier to pass around. The app logic is structured into components that generally take the entire "state" map as their input and extract what they need (similar to the Component library in regular clojure).

2. Event is the core.async pub/sub channel, and "subscribe-to" is a macro to easily set up listener go blocks for the given event.

3. In Reagent, a component can return either its markup or a fn with the same parameters and that fn will be called when it needs to be rendered. This allows you to setup closures for local state, and I also take advantage of this to just setup event handlers for the component, like in [main-menu] which has no local state.



>
> In the more general case, there are components that need to be shown/hidden (tabs, validation pop-up errors etc.). In dom-manipulation world I would set css classes to change style's visibility for example, is this idiomatically done through flags in the application state?
>

Another way to do it though, is to simply render just the DOM you need for something like a tab view (rather than rendering all tabs and switching using a CSS class). I mentioned this in your thread the other day, but as a general rule what I do is use the CSS classes if I need to take advantage of animations (like an off-canvas menu) and just render the actual DOM needed (based on flag(s) in application state) for things like tabs, main content view, etc.

With Reagent you could have an atom representing the current tab and store the component name in there (as a symbol) and then just reference it like any other component. Whenever some part of your app updates that state, then it will automatically be re-rendered. I'm not sure if that makes sense, but he does that in the routing example I linked to in my previous response.

> I am stumped as to how routing navigation fits into something like om. Again, is it a case that the navigation handlers simply update the application state? (You can see a theme in my thinking here!)

Your thinking appears to be on track in that in general, you modify app state and then your UI components are just a pure functional transformation from that app state to React's virtual DOM.

>
> In terms of reagent is it true to say that it is a bit less opinionated about these things and where-as om has a very opinionated approach to front-end state management (happening to use om), reagent is a (very nice) wrapper to om? Not to trivialize reagent, but is is "simply" trying to introduce clojurescript to react?

Addressed this in my previous reply.

> Is it also true to say that whilst om wants to manage the whole application, reagent allows you to think about disconnected bits of your app?

Yes, and that's why I like the Reagent approach better - I can modularize and reason about different functional areas. Even if I choose to bundle up all the atoms together into a map to make them easier to pass around, like I showed in my gist, I still have that choice and can develop each logical component separately without them having to know about each other or how they mix together in the overall app state. During app startup, I explicitly gather and initialize the components I am using for that app.

>
> FWIW - reagent appeals to my pragmatic "need to get stuff done" and it feels very un-opinionated and very lightweight. However, the more I read about om the more it jives with me. However, I am in the pattern of "yeah, that is how I would solve that problem", I just can't quite connect the dots in the bigger picture.
>

It sounds like you're where I was before I really dug in and tried Reagent - "yeah, it looks easy, but it's probably TOO easy and I'll paint myself into a corner and regret it halfway through the project". I have not found that to be the case at all.

Colin Yates

unread,
Oct 24, 2014, 1:54:46 AM10/24/14
to clojur...@googlegroups.com

Thank you all, kudos to Mike again!

You are right,  a spike is an excellent idea.

--
Note that posts from new members are moderated - please be patient with your first post.
---
You received this message because you are subscribed to a topic in the Google Groups "ClojureScript" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojurescript/ozK9OJTaanQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojurescrip...@googlegroups.com.

Sean Corfield

unread,
Oct 24, 2014, 3:12:02 AM10/24/14
to clojur...@googlegroups.com
On Oct 23, 2014, at 6:04 AM, Colin Yates <colin...@gmail.com> wrote:
> In terms of reagent is it true to say that it is a bit less opinionated about these things and where-as om has a very opinionated approach to front-end state management (happening to use om), reagent is a (very nice) wrapper to om? Not to trivialize reagent, but is is "simply" trying to introduce clojurescript to react?

We started off with Om / Sablono at World Singles and then switched to Reagent. Learning Om first _really_ helped us understand how React.js works but we felt that there was a lot of boilerplate with all the `reify` and method declarations, as well as the (then) restrictive argument lists and state management (since partially addressed with reference cursors). It all worked, it was all consistent, but it was just a lot of (extra) code.

Switching to Reagent allowed us to keep the same Hiccup-style DOM generation without an extra library, as well as dramatically simplifying our code base. Reagent made it much easier for us to manage local state, as well as passing arbitrary pieces of state down the component tree (because of allowing arbitrary argument lists). On the other hand, when you need to break out into React.js's lifecycle methods (e.g., when embedding D3 components), it's a bit more painful / ugly / inconsistent in Reagent than in Om.

> Is it also true to say that whilst om wants to manage the whole application, reagent allows you to think about disconnected bits of your app?

I think ref-cursors will change Om quite a bit in that area but basic Reagent has no cursors and that is a drawback. I submitted a PR to add a simple cursor to Reagent - it's been merged to master but not yet released (although it's in whoops/reagent 0.4.4-alpha). We rely on this pretty heavily to pass views of the global state down into components, so that we can have very modular code.

I would definitely recommend building a small-to-medium app using both approaches and see which you prefer. Om is a fantastic piece of work and has really blazed a trail, but Reagent may suit you better, depending on what you're trying to build and what style of ClojureScript code you prefer.

Sean Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)



Matt Ho

unread,
Oct 24, 2014, 11:45:54 PM10/24/14
to clojur...@googlegroups.com
We ended up with a similar path with other folks that posted. We started with Om, but found it brought with it a lot of incidental complexity. Switching to Reagent has been very simple and we've been very happy with the transition.

For us, the code is understandable quickly, feels like less of a burden to maintain, and productivity has gone up substantially

M

Gary Verhaegen

unread,
Oct 25, 2014, 4:12:29 AM10/25/14
to clojur...@googlegroups.com
I really like quiescent. I don't know why it's almost never mentioned in these discussions.
--
Note that posts from new members are moderated - please be patient with your first post.
---
You received this message because you are subscribed to the Google Groups "ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojurescrip...@googlegroups.com.

Colin Yates

unread,
Oct 25, 2014, 4:23:35 AM10/25/14
to clojur...@googlegroups.com

Hi Gary, now you have mentioned it :), can you explain what you really like about it?

You received this message because you are subscribed to a topic in the Google Groups "ClojureScript" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojurescript/ozK9OJTaanQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojurescrip...@googlegroups.com.

Peter Jaros

unread,
Oct 25, 2014, 1:23:49 PM10/25/14
to clojur...@googlegroups.com
On Thu, Oct 23, 2014 at 10:59 PM, Mike Haney <txmik...@gmail.com> wrote:
> Reagent is more than just a thin veneer over React. It does quite a bit of work, just as Om does. This is subjective, but Reagent feels more easy than simple to me. Sometimes, that’s what you want; sometimes it’s not. But: I’ve barely touched Reagent myself, so take that with a large grain of salt. I’d love to hear a response from someone actually working with Reagent.

OK, I'll bite.  I'm bothered by your use of the term "easy".  As I'm sure you know, that has a negative connotation in our community, and your remarks seem to imply that Reagent is a toy not to be taken seriously for non-trivial apps.  That is certainly not the case.

Yeah, I don't think I said that well. In my experience, there often comes a point where trading some simplicity for some ease is a *good thing*. Sometimes a using a simple library just means pushing the complexity into your application code, and an "easy" style approach can help deal with that. Being easy can be useful, when it's not taken to extremes. I certainly didn't mean any disrespect for Reagent.

All that said, I think I may misunderstand how Reagent works, so I'll gladly retract the whole statement. :) What you've said about it in this thread is far more useful than what I had to say.

Peter

Walter van der Laan

unread,
Oct 26, 2014, 6:13:49 AM10/26/14
to clojur...@googlegroups.com
Like Gary I also prefer quiescent. It offers a simpler programming model while still providing the speed benefits of React.

Quiescent code is simpler for two reasons. I almost never bother to implement React lifecycle functions (but if I really need to, I can), and, there is no encapsulated state in the components, as Luke VanderHart illustrates in this picture: https://github.com/levand/quiescent/blob/master/doc/diagram.png

Quiescent provides the most important speed benefits since (1) rendering is limited by the browser refresh rate and (2) React diffing is fast because components use shouldComponentUpdate in combination with cljs equality
Reply all
Reply to author
Forward
0 new messages