[ANN] Cloact 0.1.0 - Yet another React wrapper for ClojureScript

236 views
Skip to first unread message

Dan Holmsand

unread,
Jan 10, 2014, 9:19:33 AM1/10/14
to clojur...@googlegroups.com, clo...@googlegroups.com
Cloact is a minimalistic interface between ClojureScript and React.js, that now has a proper introduction, some documentation and a few examples here:

http://holmsand.github.io/cloact/

Project page and installation instructions are here:

https://github.com/holmsand/cloact

Enjoy,

/dan

David Nolen

unread,
Jan 10, 2014, 11:23:19 AM1/10/14
to clojur...@googlegroups.com, clo...@googlegroups.com
Looks very nice :)
--
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 10, 2014, 11:30:12 AM1/10/14
to clojur...@googlegroups.com, clo...@googlegroups.com
On Friday, January 10, 2014 5:23:19 PM UTC+1, David Nolen wrote:
> Looks very nice :)

Thanks!

/dan

Peter Taoussanis

unread,
Jan 11, 2014, 10:58:44 AM1/11/14
to clo...@googlegroups.com, clojur...@googlegroups.com
Hi Dan,

This seems like a really smart approach to me. I've been playing with a number of ways of structuring a large Cljs application recently - and the way Cloact cooperates with atom derefs is pretty inspired. It's flexible, it's fast, it's natural - and it makes transition from a non-React codebase easy (trivial in my case since I already using Hiccup).

Seriously great stuff, thank you for sharing this!

BTW for those looking for single-state snapshots (for undos, tooling, debugging, etc.) - all you'd need to do is merge any individual atom snapshots that constitute your full application state. It should be easy to extend Cloact (or something like Reflex https://github.com/lynaghk/reflex) to do the state tracking automatically.

Cheers! :-)

- Peter Taoussanis

David Nolen

unread,
Jan 11, 2014, 11:10:04 AM1/11/14
to clojur...@googlegroups.com
On Sat, Jan 11, 2014 at 10:58 AM, Peter Taoussanis <ptaou...@gmail.com> wrote:
BTW for those looking for single-state snapshots (for undos, tooling, debugging, etc.) - all you'd need to do is merge any individual atom snapshots that constitute your full application state. It should be easy to extend Cloact (or something like Reflex https://github.com/lynaghk/reflex) to do the state tracking automatically.

Cheers! :-)

- Peter Taoussanis

Not to detract from the Cloact approach but the problem here is having to do the merge by hand and thus that logic for snapshotting and reinstating will need to be replicated from application to application. It could be abstracted away, but this is the type of additional complexity that I am interested in avoiding.

The other option in Cloact is to use a single atom approach, but then you have mixed universe of snapshottable components and ones which aren't. 

Also a model that emphasizes local state like this, which React suffers from as well, can not leverage as many rendering optimizations in the future as Om.

So as usual ... tradeoffs :)

David

Dan Holmsand

unread,
Jan 11, 2014, 12:04:27 PM1/11/14
to clojur...@googlegroups.com, clo...@googlegroups.com
Thanks a lot Peter!

Btw, there is already code that comes from Reflex in Cloact (even if it is mangled quite a bit by now). Very undocumented, and probably a very poor api, though... See ratom.clj/.cljs. There is for example a run! macro that will execute its body every time a deref'ed atom is changed.

Quite another thing is if it is a good idea to do that :-) If possible, I'd say it is better to keep state that belongs together in a single atom.

/dan

--
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/4b8ZL_4P4ZA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojurescrip...@googlegroups.com.

Dan Holmsand

unread,
Jan 11, 2014, 12:12:20 PM1/11/14
to clojur...@googlegroups.com
On 11 jan 2014, at 17:10, David Nolen <dnolen...@gmail.com> wrote:
> Also a model that emphasizes local state like this, which React suffers from as well, can not leverage as many rendering optimizations in the future as Om.
>
> So as usual ... tradeoffs :)

It's not really meant to *emphasize* local state, just to give you a choice of where to keep the state.

But choice itself may indeed bring unwanted results sometimes (I have even made stupid choices myself a couple of times...).

/dan

Peter Taoussanis

unread,
Jan 11, 2014, 12:51:41 PM1/11/14
to clojur...@googlegroups.com, clo...@googlegroups.com

> Quite another thing is if it is a good idea to do that :-) If possible, I'd say it is better to keep state that belongs together in a single atom.

Am looking at this from a performance point of view. You're marking components as dirty when there's any change in an atom being deref'ed by the component, right?

But I'm guessing it's quite common for a component to be dealing with only a small subset of the data within a larger state atom. (This is what Om's cursors are addressing, it seems). In that case (please correct me if I'm wrong) - the number of needless rerenders will grow with the number of component-irrelevant bits of information in each atom.

One simple way of addressing that is to use more, smaller atoms - or to use "views" on atoms (as Reflex does). For example instead of marking a component as dirty when @my-atom changes, we could mark it as dirty only when `(:relevant-submap @my-atom)` changes.

Does that make sense, or am I misunderstanding something?

Dan Holmsand

unread,
Jan 11, 2014, 1:15:11 PM1/11/14
to clojur...@googlegroups.com, clo...@googlegroups.com
On 11 jan 2014, at 18:51, Peter Taoussanis <ptaou...@gmail.com> wrote:
>
> Am looking at this from a performance point of view. You're marking components as dirty when there's any change in an atom being deref'ed by the component, right?
>
> But I'm guessing it's quite common for a component to be dealing with only a small subset of the data within a larger state atom. (This is what Om's cursors are addressing, it seems). In that case (please correct me if I'm wrong) - the number of needless rerenders will grow with the number of component-irrelevant bits of information in each atom.
>
> One simple way of addressing that is to use more, smaller atoms - or to use "views" on atoms (as Reflex does). For example instead of marking a component as dirty when @my-atom changes, we could mark it as dirty only when `(:relevant-submap @my-atom)` changes.

Right, you could do that (there is a "reaction" macro in ratom.clj that is exactly a view on atoms).

But it would probably be better to just pass (:relevant-submap @my-atom) to a sub-component. The subcomponent will only be re-rendered when its arguments (i.e the params map and possible children) changes.

Also, React is fast enough that a few re-renderings won't even be noticeable most of the time. Unless React actually has to change the DOM the cost is very low.

/dan

Peter Taoussanis

unread,
Jan 11, 2014, 1:27:40 PM1/11/14
to clojur...@googlegroups.com, clo...@googlegroups.com

> But it would probably be better to just pass (:relevant-submap @my-atom) to a sub-component. The subcomponent will only be re-rendered when its arguments (i.e the params map and possible children) changes.

Ahh, gotcha. Of course, thank you!

> Also, React is fast enough that a few re-renderings won't even be noticeable most of the time. Unless React actually has to change the DOM the cost is very low.

Okay, also good to know. Very excited to start playing with this properly soon.

All the best, cheers! :-)

Peter Taoussanis

unread,
Jan 12, 2014, 8:50:11 AM1/12/14
to clojur...@googlegroups.com
Hi David,

> Not to detract from the Cloact approach but the problem here is having to do the merge by hand and thus that logic for snapshotting and reinstating will need to be replicated from application to application. It could be abstracted away, but this is the type of additional complexity that I am interested in avoiding.

It's been my experience that this kind of snapshotting is generally trivial (order of a few lines), quite simple, and usually something I'd want to be doing manually anyway since usually not _all_ state is or should be subject to undo and/or tracking (non user-induced state pushed from the server, for example).

I'd definitely agree with you if we were talking about a multi-threaded environment, btw. There the increased difficulty of reliable snapshotting might make the always-single-state-container approach a more reasonable default.

As you say, it's a matter of trade-offs: I wouldn't suggest that one approach is objectively superior to the other. Om's beautifully implemented, but (completely subjective comment here) - I found the approach was introducing a relatively significant amount of conceptual overhead. It's at an early stage, absolutely - so that criticism may not be relevant long term.

Anyway, I do find the flexibility of choosing how+where state is stored to be a plus in practice, especially during prototyping stages and when migrating from an existing code base.

> The other option in Cloact is to use a single atom approach, but then you have mixed universe of snapshottable components and ones which aren't. 

I'm quite happy with having that flexibility since it allows me to make trade-offs as/when I need them.

> Also a model that emphasizes local state like this, which React suffers from as well, can not leverage as many rendering optimizations in the future as Om.

I'm curious what you have in mind here and why you're describing it as "local" state. If I've understood React's diff algo correctly, it seems so long as we're handling our dirty/not-dirty marking accurately, we're subject to "all" optimisations, always. Since the atoms are immutable and their comparisons done as identity checks, I'm not seeing how much room there'd be for improvement driven by ClojureScript's side?

Again, not at all criticising - think Om's wonderful and would love to have any misunderstandings corrected.

Have an awesome day, cheers! :-)

- Peter

David Nolen

unread,
Jan 12, 2014, 1:51:11 PM1/12/14
to clojur...@googlegroups.com
On Sun, Jan 12, 2014 at 8:50 AM, Peter Taoussanis <ptaou...@gmail.com> wrote:
It's been my experience that this kind of snapshotting is generally trivial (order of a few lines), quite simple, and usually something I'd want to be doing manually anyway since usually not _all_ state is or should be subject to undo and/or tracking (non user-induced state pushed from the server, for example).

In Om you will be able to pick what you want to snapshot. But I'm planning on providing a higher level api which will provide some interesting facilities :)
 
Anyway, I do find the flexibility of choosing how+where state is stored to be a plus in practice, especially during prototyping stages and when migrating from an existing code base.

You have the same flexibility in Om. Cloact just provides more admittedly nice sugar.
 
> The other option in Cloact is to use a single atom approach, but then you have mixed universe of snapshottable components and ones which aren't. 

I'm quite happy with having that flexibility since it allows me to make trade-offs as/when I need them.

Then I won't use your components :) 

> Also a model that emphasizes local state like this, which React suffers from as well, can not leverage as many rendering optimizations in the future as Om.

I'm curious what you have in mind here and why you're describing it as "local" state. If I've understood React's diff algo correctly, it seems so long as we're handling our dirty/not-dirty marking accurately, we're subject to "all" optimisations, always. Since the atoms are immutable and their comparisons done as identity checks, I'm not seeing how much room there'd be for improvement driven by ClojureScript's side?

If you read up on React's diff'ing algorithm you'll see there are some missed opportunities. In Om we will always know what's changed and precisely how it's related to what the user sees so we can drive React's diff'ing algorithm much better.

David

Moritz Ulrich

unread,
Jan 12, 2014, 2:22:51 PM1/12/14
to clojur...@googlegroups.com

David Nolen writes:

> On Sun, Jan 12, 2014 at 8:50 AM, Peter Taoussanis <ptaou...@gmail.com>wrote:
>
> In Om you will be able to pick what you want to snapshot. But I'm planning
> on providing a higher level api which will provide some interesting
> facilities :)

Care to tell us a bit more about your plans? :)

--
Moritz Ulrich

David Nolen

unread,
Jan 12, 2014, 2:35:34 PM1/12/14
to clojur...@googlegroups.com
I've started outlining things out in the "chronos" branch - https://github.com/swannodette/om/tree/chronos, the current protocol sketch gives some idea of what I'm considering - https://github.com/swannodette/om/blob/chronos/src/om/chronos.cljs

David

Peter Taoussanis

unread,
Jan 13, 2014, 1:34:32 AM1/13/14
to clojur...@googlegroups.com
> Then I won't use your components :) 

I guess I'd feel sad if that became a choice you wouldn't be able to make.

Anyway, these are early days and I think it'd be silly for me to have strong opinions. I'm quite excited at the prospect of different ideas being explored and encouraged, and I'm very excited to see what awesomeness you cook up with Om (as you always do) :-)

Cheers
Reply all
Reply to author
Forward
0 new messages