

Om Sweet Om: functional front-end engineering with ClojureScript and React - trevoragilbert
http://blog.getprismatic.com/om-sweet-om-high-functional-frontend-engineering-with-clojurescript-and-react/

======
skrebbel
Thanks for this post! I'm very inexperienced with Clojure, but I'm a big fan
of React. I've always wanted to understand how Om encourages immutability and
how it deals with "UIs are stateful by definition", but I never managed to
wrap my head around it, probably due to my missing Clojure skills.

Your post explained it in general terms. I will probably apply the same in
plain JS now. It sounds like an interesting challenge to implement the Cursors
idea using React's Update addon [1] instead of depending on Mori. (I don't
like Mori because I don't get the feeling that it's intended to be used
without ClojureScript - its API is rather cumbersome from plain JS)

[1]
[http://facebook.github.io/react/docs/update.html](http://facebook.github.io/react/docs/update.html)

~~~
swannodette
I'm curious about the "cumbersome" claim. Is it because Mori forces you to
write in a functional style? I've tried as much as possible to supply an API
that doesn't stray too far from conventions JavaScript programmers are used to
from underscore, lo-dash, etc.

~~~
skrebbel
Oh, it might have very well been me being to much of a newbie to even
understand what was going on, so please don't take the claim too seriously.

I _believe_ that it had something to do with it being relatively much work to
update things deep in a tree.

Like, if I have Model -> Users -> User -> WallUpdates -> WallUpdate ->
Comment, and I want to make an update to a comment, I'd have to do a lot of
code writing. Is that true, or did I just miss the magic function that helps
me do this?

But I'm now realizing that maybe, if I want to "manually" do that, I'm just
doing it wrong - this should partly be what Om's cursors are meant to avoid,
right? (this, and locality to components)

In general, if it helps you as feedback, I found Mori a little difficult to
approach, having no Clojure experience. I suspect that some more examples of
typical usage patterns at the top of the docs would do a lot. Like, which
methods are supposed to be commonplace, and which ones are more for edge
cases? What kind of things am I supposed to do all the time, what kind of
things should I wish to avoid except in these and these cases?

FWIW, I feel like the same holds for Underscore/Lo-Dash's docs, except it's
been less of a problem for me because I'm more familiar with typical usage
patterns of mutable data structures.

~~~
swannodette
Ah, Mori provides a few useful functions for updating nested structures -
update_in and assoc_in.

[http://swannodette.github.io/mori/#assoc_in](http://swannodette.github.io/mori/#assoc_in)

[http://swannodette.github.io/mori/#update_in](http://swannodette.github.io/mori/#update_in)

~~~
mnemonik
While we're on the subject, here's my super nitpicky complaint abour mori: the
use of snake_case when almost all JS is camelCase.

In the simple little project where I used mori, I did this:

    
    
        const {
          get,
          conj,
          js_to_clj: map,
          clj_to_js: pretty
        } = mori;
    

But I bet you lose a surprisingly large number of JS devs due to just having
the "wrong" names. Maybe I should publish a camel-case-mori module on npm...

~~~
city41
This is because mori is written in ClojureScript. For the most part, all mori
is doing is exporting pieces of Clojure so that the native JavaScript
environment can access them[1]. In Clojure, the naming convention is "foo-
bar", and the cljs compiler translates that to "foo_bar".

I can't imagine changing the cljs compiler or adding a piece to mori that must
be maintained is important enough for this. I'd imagine Swannodette's answer
would be "you should use ClojureScript anyway" :)

[1] --
[https://github.com/swannodette/mori/blob/master/src/mori.clj...](https://github.com/swannodette/mori/blob/master/src/mori.cljs)

~~~
sgrove
I don't think it'd be a huge issue in any case, probably just a case of
providing a handler for name conversions. Not sure if it's worth the extra
code lying around, but anything that potentially makes cljs a better actor in
the js world is nice.

------
dustingetz
fwiw it's possible to achieve all of these benefits in vanilla react using
javacsript with regular javascript data structures

    
    
        * single mutable ref to normalized app state
        * cursors for encapsulation and modularity
        * O(1) deep equality checks
        * fastest possible react performance
    

source: I have done it in two large react apps, but i have not documented it
yet. Blog post coming soon.

Interestingly, as your app state gets bigger, you are actually forced to use
something like cursors for all state updates, for performance reasons. Naive
immutability in javascript means clone-before-modify, which is far too slow
when your state values are large. It quickly bubbled up in our profiler.
Funneling all immutable updates through a cursor means the cursor can use
structure sharing on the state value, skipping the defensive clone and
preserving reference equality for equal subtrees. Also afaik, implementing a
general shouldComponentUpdate (edit: in js) without something like cursors is
not possible, because you need === equality for onChange props.

~~~
skrebbel
One question I have for you: In those large React apps, did you put _all_
state changes in the big single central app state data structure?

I'm currently building an app where the core state is simply a tree of plain
old mutable JS objects. We have all the expected disadvantages (mostly: no
easy shouldComponentUpdate), but things are easy to reason about, otherwise.
We found that stuff like "has the user expanded a dropdown" or "which auto-
completeable search term has just been entered" works perfectly in a local
component's setState. The idea is, basically, anything that the user doesn't
care losing when navigating away from the current screen can go in a local
component's state. This seems to go entirely against the functional
immutability idea, but it significantly reduces the amount of updates we need
to do to our big central data structure.

I'm going to build a new thing and I'm considering going the nice functional
way. However, I can't see any downsides to using directly mutable state to
simple, local, unimportant stuff.

Would you do it like that? Or would you avoid component state altogether and
go with props only?

~~~
dustingetz
We also use component local state for things like that, and David Nolen (Om)
likes component local state too. But the state goes up top by default. The
biggest reason for this is because you might want to attach a diagnostic
component to your state (e.g. a json editor [1]), and you don't want to have
to change the shape of your state to do that.

[1] [https://github.com/dustingetz/react-json-
editor](https://github.com/dustingetz/react-json-editor)

------
w01fe
Engineer @ Prismatic here. I'm happy to answer any questions about the post or
our experiences with the rewrite.

~~~
dkersten
An Om question: what do you store in the app state and what do you store in
component local state?

In my own application, I started off storing everything in app state, using
local state only for short-lived transient data, but over time I found that
having a bunch of information like what tab is currently visible in the app
state became difficult to manage and made the app state a nightmare. So now I
like to keep a strict separation: app state is basically my model - the data
being processed (if I were writing a text editor, its the document being
edited) and local state is everything else: which dialog is open, which tab is
selected, button toggle state, mouse state etc (in a text editor, whether bold
is selected, whether I'm in print preview mode or edit mode, etc). Basically
app state is _what_ is being processed and local state is _how_ to process or
display it. This separation also means that the app state is what gets synced
with the server. I like to think of it as _" if its something I might want to
persist in the database, then it lives in app state, if its something I would
never want in the database, then it lives in local state"_

I'm curious what other peoples experience with this is.

Another Om question: how do you sync state between client and server, or how
do you handle communication?

I looked at how om-sync does this (using tx-listen to automatically sync
changes), but ended up building a more traditional request/response model
using sente[0] and having my app ask for sub-trees of my app state as needed.
Again, just wondering about peoples experience with this and different
approaches tried.

[0]
[https://github.com/ptaoussanis/sente](https://github.com/ptaoussanis/sente)

~~~
w01fe
We're still figuring out exactly where the boundary lies, but our current
approach is much like yours -- view state that's purely local to a component
goes in local state, and everything else goes in app state. This feels more
natural, but means you lose some of the nice benefits of things like
replay/undo for this local state.

For client/server, we've started building up some nice abstractions around our
API (like fetching more data in a generic paginated list stored in a cursor),
but the write side is still mostly manual. We're trying to use the same API
for iOS and web, which probably prevents us from doing some fancier things
that continue the same architecture back into the server.

~~~
fnordsensei
So far, I've only made some (large-ish, granted) prototypes with Cljs and Om,
but I've run into this as well. I ended up opting out of local state. I put
view state and model state in the same atom, but I tried my best to keep them
separate within the atom.

Easy replay/undo was one reason. Another reason was that I wanted to be able
to take a _complete_ snapshot of the app state, for debugging purposes. A
third was that I wanted to keep all the state that a component needs to know
in order to render itself in one place, not many places.

------
jlehman
I've had the same experience building
[http://fitsmeapp.com](http://fitsmeapp.com) with Om/Clojure -- great to see
real, production apps being built in CLJS!

~~~
acconrad
I think your sprite for your logo is all messed up (looks way too large). Also
have you heard of [http://www.eatthismuch.com/](http://www.eatthismuch.com/) ?
How does yours differ?

~~~
jlehman
Just learned about that recently -- it's a retina display issue (fix going up
tonight). Thanks for pointing it out!

I have heard of it and used it myself.

The primary difference is really the intended use case. Where ETM is focusing
on highly-specific (usually on a nutrient/caloric basis) meal plans to hit a
target nutritional intake, we are more centered around using general
preferences about food (ingredients, cuisine, cooking methods, etc.) in a
"browsing" capacity -- currently in the form of recipes aggregated from other
sites and bloggers. The goal is eventually to also incorporate nearby
restaurant dishes.

------
andrejewski
For people who have wanted to try this type of programming out but have not
jumped the boat from JavaScript to ClojureScript, I created a little library
that mixes Backbone and React in a similar model described in the article:
[https://github.com/andrejewski/reactbone](https://github.com/andrejewski/reactbone)

It takes away a lot of the data-binding cruft that you find in Backbone.View
and works well with React without making you switch languages, design
paradigms, or add new libraries. The Backbone component makes it easier to
build data hierarchies than with plain JS, but I assume other MVC frameworks
could be retrofitted to work with React.

~~~
cnp
Examples would be lovely

------
Touche
It's going to be much nicer when ClojureScript runs in Node without needing a
JVM. Now a ClojureScript project requires a lot of boilerplate and running
cljsbuild auto. For big projects this is fine but I want to write smaller
scripts in ClojureScript as well. Basically I want to do:

    
    
       cljsbuild app.cljs > app.js
    

And that's all. No project.clj, no waiting 5 seconds for clojure/jvm to load
and Closure to compile it all.

~~~
lennel
clojurescript run in node? is this on the roadmap?

~~~
dkersten
ClojureScript already runs on node:
[https://github.com/clojure/clojurescript/wiki/Quick-
Start#ru...](https://github.com/clojure/clojurescript/wiki/Quick-
Start#running-clojurescript-on-nodejs)

I think what GP is complaining about is the compilation step, which requires a
JVM and isn't exactly the fastest.

Touche - You don't need project.clj and leiningen and can run a command
basically like you're asking for. I'm not sure how much faster it is than
using leiningen and cljsbuild, as I haven't tried it:
[https://github.com/clojure/clojurescript/wiki/Quick-
Start#cl...](https://github.com/clojure/clojurescript/wiki/Quick-
Start#clojurescript-compiler)

~~~
lennel
this is what I was reffering too, the compilation step. It seems very daft for
me to loose the closure compiler. I assume lein keeps the jvm hot, if not
writing a script to do it should be pretty simple.

~~~
dkersten
`cljsbuild auto` keeps the jvm hot and rebuilds any time the source changes.

You could also use something like Nailgun too, if that is more desirable.

When I develop in ClojureScript, I generally avoid rebuilding altogether
whenever possible (auto or otherwise) and instead have my editor send changes
to the browser REPL. I don't know if you can do this with node too, but it
would seem odd if not. This seems to be the preferred approach by most Clojure
devs.

~~~
Touche
Yes, but I factor in having to create a folder structure, edit the project.clj
file to add my compilation targets, and all of that, it's not "light weight"
for something like a small script that just is a single widget.

Btw, I ran: time lein cljsbuild once on a project with a minor change (added a
line break) and it took 22 seconds.... so this is also what I'm talking about.

------
kylebrown
I'm real curious about the potential interplay between core.async and
om/react. Bruce Hauman has a great tutorial on using core.async channels (as
opposed to event callbacks) to process user input and gestures.[1] David Nolen
(creator of Om) also covered core.async.[2] But these tutorials pre-date Om;
core.async came to clojurescript in summer 2013, Om wasn't released until
2014.

I guess the way React handles input is by attaching event handlers to child
components, so that changes to the child properties bubble up to the root
component state. Is this the optimal pattern for handling events in a React
app? Or is there an advantage to be gained by using core.async channels to
process event input for Om/react?

I haven't seen any examples which use both core.async _and_ Om/react, but I'm
very curious about the possibility.

1\. [http://rigsomelight.com/2013/07/18/clojurescript-core-
async-...](http://rigsomelight.com/2013/07/18/clojurescript-core-async-
todos.html)

2\. [http://swannodette.github.io/2013/07/12/communicating-
sequen...](http://swannodette.github.io/2013/07/12/communicating-sequential-
processes/)

~~~
presty
> I haven't seen any examples which use both core.async and Om/react, but I'm
> very curious about the possibility.

that's strange, because Om's tutorials use core.async

[https://github.com/swannodette/om/wiki/Basic-
Tutorial](https://github.com/swannodette/om/wiki/Basic-Tutorial)

[https://github.com/swannodette/om/wiki/Intermediate-
Tutorial](https://github.com/swannodette/om/wiki/Intermediate-Tutorial)

~~~
kylebrown
Ah yup, there it is in the basic tutorial, "Intercomponent communication". It
is strange that I don't remember noticing that when I skimmed it. I must've
been too distracted counting parentheses and trying to wrap my head around
clojure syntax.

I'm relieved to know it was there the whole time and I simply missed it, thank
you.

------
haberman
One thing that's unclear to me: does this require the entire application state
to be loaded into memory all the time?

If your application state is large but you only care about a few parts of it
at a time (a few "cursors") how does Om deal with that?

~~~
swannodette
Not sure what you mean? Om uses whatever state information has been delivered
to the client browser same as other JavaScript approaches/frameworks.

~~~
haberman
I saw some stuff about cursors as a way of displaying/modifying only a part of
the global state. But it wasn't clear to me whether this is a formalized way
of only _loading_ part of the global state, or if cursors still require that
the entire underlying "global state" is loaded into memory.

\--

To give more background, let me give more specifics over what I'm thinking
about when I ask this.

I've been working on a JS accounting app. One thing about accounting is that
the balance of an account is dependent on every prior transaction that the
account has ever had. So modeled in functional terms, it seems like you'd have
to make this a tree of depth "N" where N is the total number of transactions
in the account. It seems like you have to model it this way to get the data
dependencies right (a change in an early transaction affects every subsequent
transaction).

A functional approach with such a deep tree seems to break down (at least from
the relatively little I know about Om). For one, I don't want to have to load
every transaction into memory. Even though the most recent transaction depends
logically on the first one, I don't want to be forced to load the first one
just to show the most recent one. One possible way to work around this is to
collapse all transactions prior to the ones I want to display into a single
"fake" transaction that stores the accumulated balance as of this point. But
now my in-memory model has to differ from my real model, and as the two
diverge things start to get more complicated.

A bigger problem: I can't think of a good way, with this design, to indicate
to the data layer which transactions the UI actually cares about and wants to
have loaded into memory. If the tree has only one logical root (the most
recent transaction) and is logically N transactions deep, how does the UI
indicate which of those N it actually cares about? Maybe Om cursors can do
this somehow?

I've been using a more traditional mutation-based/observable design, which
seems to be working well. The UI can "subscribe" to certain
transactions/balances, and this serves as a signal to the data layer about
what needs to be loaded and what derived values need to be kept up to date.
The data layer can be smart about how many transactions it actually needs to
load to compute what the UI cares about.

~~~
omegaworks
I feel like a lazily loaded datastructure would solve this problem - you only
need the last k transactions, then it only loads the last k transactions...

With regard to Om or Clojure I'm not sure of the specifics of such an
implementation, but doing something like that with other languages that have
functional programming support is one of the Major Selling Points.

------
presty
I'm interested in knowing more about how people are architecturing their Om
projects

e.g. are you using something like:

app component <-> page components <-> (widget) components

where the app creates the page and the page creates the widgets and
communication is flowing through channels?

and how do you deal with url transitions?

do you capture and flow up the click from a widget back to the app so it knows
it's time to render a different page? and have some (case ...) at the app
level?

or using some kind of mix with secretary and changing the location.href/a
hrefs?

etc

~~~
w01fe
Yep, that sounds about right. For us, API communication and some global events
flow through channels into the app state, where it's distributed to
components. For routing we've built up a library around secretary, where we
write down a map of uris, handlers, and metadata (can be a modal, etc). Info
about the current page and such goes into the app state and that drives the
transitions between page components.

