Reagent port of the Om tutorial

1,956 views
Skip to first unread message

Jonas Enlund

unread,
Jan 25, 2014, 1:50:24 AM1/25/14
to clojur...@googlegroups.com
Hi

As an exercise I ported the Om tutorial to Reagent. It's available at https://github.com/jonase/reagent-tutorial

I hope you find it interesting.

Jonas

David Nolen

unread,
Jan 25, 2014, 2:49:56 AM1/25/14
to clojur...@googlegroups.com
Nice. I do consider the non-modularity of `update-contacts!` here to be one of the big things I try to address in Om. The Reagent `update-contacts!` knows too much. In Om, it doesn't matter at all where :contacts lives in the app state, the Om contacts-view can still update it.

David



Jonas

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

Dan Holmsand

unread,
Jan 25, 2014, 3:27:05 AM1/25/14
to clojur...@googlegroups.com
Looks great!

/dan

Jonas Enlund

unread,
Jan 25, 2014, 3:57:24 AM1/25/14
to clojur...@googlegroups.com
On Saturday, January 25, 2014 9:49:56 AM UTC+2, David Nolen wrote:
> Nice. I do consider the non-modularity of `update-contacts!` here to be one of the big things I try to address in Om. The Reagent `update-contacts!` knows too much. In Om, it doesn't matter at all where :contacts lives in the app state, the Om contacts-view can still update it.

So if I understand correctly the 'app' arg in (defn contacts-view [app owner] ...) doesn't have to be the root of the app-state atom?

Moritz Ulrich

unread,
Jan 25, 2014, 9:57:16 AM1/25/14
to clojur...@googlegroups.com

Jonas Enlund writes:

> On Saturday, January 25, 2014 9:49:56 AM UTC+2, David Nolen wrote:
>> Nice. I do consider the non-modularity of `update-contacts!` here to be one of the big things I try to address in Om. The Reagent `update-contacts!` knows too much. In Om, it doesn't matter at all where :contacts lives in the app state, the Om contacts-view can still update it.
>
> So if I understand correctly the 'app' arg in (defn contacts-view [app owner] ...) doesn't have to be the root of the app-state atom?

You understand correctly. Om implements a cursor data-structure which
allows you to pass a 'subset' (for example (:contacts state) of the
components. om/update! will update as expected (it will just see the
:contacts part of the state).

David Nolen

unread,
Jan 25, 2014, 10:38:14 AM1/25/14
to clojur...@googlegroups.com
On Sat, Jan 25, 2014 at 3:57 AM, Jonas Enlund <jonas....@gmail.com> wrote:
On Saturday, January 25, 2014 9:49:56 AM UTC+2, David Nolen wrote:
> Nice. I do consider the non-modularity of `update-contacts!` here to be one of the big things I try to address in Om. The Reagent `update-contacts!` knows too much. In Om, it doesn't matter at all where :contacts lives in the app state, the Om contacts-view can still update it.

So if I understand correctly the 'app' arg in (defn contacts-view [app owner] ...) doesn't have to be the root of the app-state atom?

It can be at any location in the app state and it will always work. This is important for component composition and re-usability.

If I was to describe the difference between Om and Reagent it would be that Om is draconian about component modularity in order to encourage component reuse. Directly manipulating the app state from a component in Om is a anti-pattern. Components communicating with each other via functions is also an anti pattern (use channels).

David 

Jonas Enlund

unread,
Jan 25, 2014, 12:34:48 PM1/25/14
to clojur...@googlegroups.com
Interesting! I can definitely see how UI components directly manipulating global state can make reuse difficult.

I guess this is not really an issue in React as their components have local state (via getInitialState). It's possible to work with local state in Reagent as well so reusable components are not completely out of the picture.

It's great if Om can work with global state without hindering reuse. That means you can (for example) update :contacts without going via the UI components at all (perhaps pushed to you from the server).

I experimented a bit with moving :contacts to the server and broadcasting changes to all connected clients here: https://github.com/jonase/reagent-tutorial/tree/server

As I write in the README. It's interesting to compare these two awesome libraries.

Thanks,

Jonas

>
>
>
> David 

David Nolen

unread,
Jan 25, 2014, 12:43:05 PM1/25/14
to clojur...@googlegroups.com
On Sat, Jan 25, 2014 at 12:34 PM, Jonas Enlund <jonas....@gmail.com> wrote:
Interesting! I can definitely see how UI components directly manipulating global state can make reuse difficult.

I guess this is not really an issue in React as their components have local state (via getInitialState). It's possible to work with local state in Reagent as well so reusable components are not completely out of the picture.

It is an issue if you want modular time management. The undo example for Reagent is not modular. All time management in Om is designed to work across any configuration of components embedded in whatever way you please.
 
It's great if Om can work with global state without hindering reuse. That means  you can (for example) update :contacts without going via the UI components at all (perhaps pushed to you from the server).

Orthogonal concern and already possible as long as the server has the app state structure, which you must have in the production case anyway when you want to pre-render.

David

Dan Holmsand

unread,
Jan 25, 2014, 1:55:03 PM1/25/14
to clojur...@googlegroups.com

On 25 jan 2014, at 18:43, David Nolen <dnolen...@gmail.com> wrote:
On Sat, Jan 25, 2014 at 12:34 PM, Jonas Enlund <jonas....@gmail.com> wrote:
Interesting! I can definitely see how UI components directly manipulating global state can make reuse difficult.

I guess this is not really an issue in React as their components have local state (via getInitialState). It's possible to work with local state in Reagent as well so reusable components are not completely out of the picture.

It is an issue if you want modular time management. The undo example for Reagent is not modular. All time management in Om is designed to work across any configuration of components embedded in whatever way you please.

Well, that depends on your definition of "modular", doesn't it :)

It is "modular" in the sense that it is isolated from the rest of the page, which is (I suppose) sort of the opposite of your idea of modular...

Anyway, the typical way to make components reusable in terms of state handling in React is to make parent components responsible for updates and fetches, and pass functions to modify state to children as parameters (e.g remove-contact! and add-contact! in Jonas' example would be local to some parent component, so that children don't have to know anything about state handling).

That way you get everything nice and configurable. The downside is some added complexity, and the temptation to make everything so configurable that it becomes unreadable and unusable :)

/dan

David Nolen

unread,
Jan 25, 2014, 2:50:45 PM1/25/14
to clojur...@googlegroups.com
On Sat, Jan 25, 2014 at 1:55 PM, Dan Holmsand <holm...@gmail.com> wrote:
Well, that depends on your definition of "modular", doesn't it :)

If I can't include somebody's component and apply time management to it, that's sounds unambiguously "non-modular" with respect to *time management*.
 
It is "modular" in the sense that it is isolated from the rest of the page, which is (I suppose) sort of the opposite of your idea of modular...

I think I was careful to say modular with respect to something specific earlier :)

In Om we get the same basic component modularity offered by React and Reagent but we also get a whole new kind modularity because Om was designed that way after a fairly serious assessment of the React model.

David 

 

Dan Holmsand

unread,
Jan 25, 2014, 4:24:20 PM1/25/14
to clojur...@googlegroups.com
On 25 jan 2014, at 20:50, David Nolen <dnolen...@gmail.com> wrote:
On Sat, Jan 25, 2014 at 1:55 PM, Dan Holmsand <holm...@gmail.com> wrote:
Well, that depends on your definition of "modular", doesn't it :)

If I can't include somebody's component and apply time management to it, that's sounds unambiguously "non-modular" with respect to *time management*.

I meant that you may not *want* to let "time management" of one component influence undo functionality of another. I think there is value in having both "integrated modules" (w.r.t for example time management), and "isolated modules", and that the line between the two may not always be very clear.

(Btw. "time management" is a very cool expression. Must use that more often :))

It is "modular" in the sense that it is isolated from the rest of the page, which is (I suppose) sort of the opposite of your idea of modular...

I think I was careful to say modular with respect to something specific earlier :)

In Om we get the same basic component modularity offered by React and Reagent but we also get a whole new kind modularity because Om was designed that way after a fairly serious assessment of the React model.

Oooh, I didn't in any way mean to say that Om is anything but extremely cool. The invention of a whole new way of handling state in client apps doesn't happen every day. Probably not even every decade.

I'm just trying to defend my little creation  :)

And I think that there is also value in the "plain React model" - it sort of fits my brain quite nicely for some reason. And it is so much nicer to use in ClojureScript (especially since it becomes easy to separate state from UI definition).

/dan

David Nolen

unread,
Jan 25, 2014, 5:41:03 PM1/25/14
to clojur...@googlegroups.com
On Sat, Jan 25, 2014 at 4:24 PM, Dan Holmsand <holm...@gmail.com> wrote:
On 25 jan 2014, at 20:50, David Nolen <dnolen...@gmail.com> wrote:
On Sat, Jan 25, 2014 at 1:55 PM, Dan Holmsand <holm...@gmail.com> wrote:
Well, that depends on your definition of "modular", doesn't it :)

If I can't include somebody's component and apply time management to it, that's sounds unambiguously "non-modular" with respect to *time management*.

I meant that you may not *want* to let "time management" of one component influence undo functionality of another. I think there is value in having both "integrated modules" (w.r.t for example time management), and "isolated modules", and that the line between the two may not always be very clear. 

Yes and the Om approach doesn't preclude that in anyway. The whole idea in Om is for an applications programmers to be able dictate time management, not components themselves.
 
Oooh, I didn't in any way mean to say that Om is anything but extremely cool. The invention of a whole new way of handling state in client apps doesn't happen every day. Probably not even every decade.

I'm just trying to defend my little creation  :)

I think we can discuss the pluses and minuses of approaches without taking it personally ;) Reagent is certainly more approachable, has features Om does not, and certainly can be used in many fantastic applications that don't want or need what Om hopes to provide.

David 

Dan Holmsand

unread,
Jan 25, 2014, 6:38:13 PM1/25/14
to clojur...@googlegroups.com
On 25 jan 2014, at 23:41, David Nolen <dnolen...@gmail.com> wrote:
> I think we can discuss the pluses and minuses of approaches without taking it personally ;) Reagent is certainly more approachable, has features Om does not, and certainly can be used in many fantastic applications that don't want or need what Om hopes to provide.

Good, exactly what I wanted. Enough with the flattering then ;)

Btw I'm slowly beginning to realize just how good your batching-updates-by-delaying-with-requestanimationframe idea is, so I'm planning to steal it. :) It just took a little time for it to make its way through my brain...

/dan

Peter Taoussanis

unread,
Jan 26, 2014, 3:41:46 AM1/26/14
to clojur...@googlegroups.com
Anecdotally, my initial experience is that Reagent's state model is very attractive on a number of levels. The effects I've seen appear to be multiplied for large+complex apps and when migrating from preexisting codebases.

There are a couple of rough spots (as there are with all wrappers currently, and with React itself) - but they're notably of the superficial & correctable-in-time variety (IMO).

I wouldn't normally advocate so strongly for something (certainly not repeatedly), but I feel inclined to here since I don't personally believe it's accurate to characterise the choices made by Reagent as in any way wrong or inferior. They're offering up a different set of tradeoffs that some might find beneficial.

Indeed, I think it'd be premature to make any strong statements at this point since so much of our development model has recently been thrown up in the air with the advent of tool combinations like Cljs + core.async + React, and the fantastic new patterns they enable. It's just way too early to be critical of new ideas at this point.

Anyway, Dan's a relatively new member of our community and I'd be sad if he felt discouraged from pursuing his ideas.

I'd encourage anyone following these discussions to check out the relevant examples and decide what makes sense for your own requirements and taste.

Tl;dr - let's try things, let's share the best of them, and let's build great stuff. Kumbaya and all that. Okay, I'll shut up now.

Peace :-)

Dan Holmsand

unread,
Jan 26, 2014, 4:22:02 AM1/26/14
to clojur...@googlegroups.com

On 26 jan 2014, at 09:41, Peter Taoussanis <ptaou...@gmail.com> wrote:

> Anecdotally, my initial experience is that Reagent's state model is very attractive on a number of levels. The effects I've seen appear to be multiplied for large+complex apps and when migrating from preexisting codebases.
>
> There are a couple of rough spots (as there are with all wrappers currently, and with React itself) - but they're notably of the superficial & correctable-in-time variety (IMO).

Thanks! Be sure to let me know about the rough spots...

> Anyway, Dan's a relatively new member of our community and I'd be sad if he felt discouraged from pursuing his ideas.

Just to be clear, I don't mind being criticized at all. I was just a little worried about coming across as arrogant (being as you say new to ClojureScript and all), by defending my little library. On the contrary, I always find it kind of flattering when smart people reflect on something I've done :)

Kumbaya,

/dan

Jonas Enlund

unread,
Jan 26, 2014, 7:13:59 AM1/26/14
to clojur...@googlegroups.com
Hi Dan

I added a section in the README where I try to explain each part of the program. If you (or someone else) find any errors in the explanations I'd like to fix them. Bug me here or open an issue (or pull request) on Github.

I find Reagent very easy and intuitive to work with. Next, I will make a more serious attempt at understanding the Om model which seems equally interesting.

All the innovation currently going on in the CLJS community is awesome and I think that both Om and Reagent is heading in the right direction.

Jonas

Dan Holmsand

unread,
Jan 26, 2014, 11:40:52 AM1/26/14
to clojur...@googlegroups.com
On 26 jan 2014, at 13:13, Jonas Enlund <jonas....@gmail.com> wrote:

> I added a section in the README where I try to explain each part of the program. If you (or someone else) find any errors in the explanations I'd like to fix them. Bug me here or open an issue (or pull request) on Github.

Great stuff, Jonas!

Just one tiny nitpick: you are calling the contact component in contact-list with "[contact c]", which works absolutely fine in this context.

But since the first argument to components has to be a map (in order to be passed on as "props", following Hiccup conventions), I would change that to something like "[contact {:item c}]" to be a bit more explicit about that.

Very nice and clear otherwise.

Cheers,

/dan

Dmitri Sotnikov

unread,
Jul 15, 2014, 10:48:57 AM7/15/14
to clojur...@googlegroups.com
One thing that's important to note is that code clarity is equally as valuable as reusability. In this particular example we're talking about making 3 lines of code reusable at the cost of adding additional complexity to the code base.

I come from Java background where the culture of making things generic for the sake of genericity is taken to the extreme. This often results in code that's much more opaque, difficult to reason about, and harder to maintain than non-reusable code would have been.

When it takes more work to reuse a components than to write them from scratch the value of reuse becomes questionable. You often end up with a lot of incidental code that obscures the purpose of the program.

While Om approach is much more prescriptive when it comes to making reusable components that doesn't always result in code that's easier to reason about and maintain.

Andrew Stoeckley

unread,
Jul 16, 2014, 8:31:37 PM7/16/14
to clojur...@googlegroups.com
On Saturday, January 25, 2014 11:38:14 PM UTC+8, David Nolen wrote:
>
> If I was to describe the difference between Om and Reagent it would be that Om is draconian about component modularity in order to encourage component reuse. Directly manipulating the app state from a component in Om is a anti-pattern.

Can I get a bit of clarification on this: The above suggests an anti-pattern but isn't the idea that app state be manipulated using om/update! or om/transact! both of which must be called inside a component function?

Or are you suggesting that it is the passing of the entire app-state to a component rather than a specific cursor that is the anti-pattern?

Jamie Orchard-Hays

unread,
Jul 17, 2014, 11:19:32 AM7/17/14
to clojur...@googlegroups.com
om/update! is working on the cursor's view/entry-point into the app state atom, not directly with the entire atom you've declared to hold your app state.

Val Waeselynck

unread,
Feb 11, 2015, 5:09:22 PM2/11/15
to clojur...@googlegroups.com
If I may, Reagent has cursors too: https://github.com/reagent-project/reagent-cursor, that allow exactly that.

Marc Fawzi

unread,
Feb 11, 2015, 6:16:29 PM2/11/15
to clojur...@googlegroups.com
We use this Reagent cursor


But will switch to Reagent cursor once it's stable, e.g. this issue https://github.com/reagent-project/reagent/issues/99

Enlightenment is always welcome.



Marc Fawzi

unread,
Feb 11, 2015, 6:19:14 PM2/11/15
to clojur...@googlegroups.com
correction:
was referring to a 3rd party cursor, not one that comes with Reagent, but we do like to move to the Reagent cursor once it's stable

Reply all
Reply to author
Forward
0 new messages