I've recently worked on back porting the "cursors" feature of Om to our React project using Facebook's immutable-js library. Although I did have some success (I got a basic POC working inside our app), the work to do a complete port of our project to this way of doing things was decided to be too much effort for the benefits it would bring.
I am still, however, a big fan of the immutable/Om way of doing things with React, and it will be the way I build future React projects from the ground up.
Some feedback on the article itself:
"For an animation, like moving a <div> from left to right, the IRenderState function might be called several times a second. Sometimes it is better to think of your app like a movie (a cartoon, I guess) rather than a static set of objects sitting around waiting for events to happen. And if you are thinking: “that must be horribly slow”… well, it isn’t, thanks to React’s virtual DOM that efficiently projects changes out into the real DOM."
If you're re-rendering a div in different places using Om/React in order to animate it, I would say you're probably doing it wrong. You should do one re-render to apply a new CSS transition class.
Also, lots of re-rendering on desktop Chrome is, usually, quite fast because of V8 and desktop CPU's... try doing this on mobile, or (in our case) a DTV set-top-box, and you'll quickly run into performance issues. Not every JavaScript engine/platform is fast enough to pull off the naive "re-render everything" approach in React. That's why they included shouldComponentUpdate, and why David Nolen went to such effort to facilitate cheap shouldComponentUpdate checks in Om.
Of course, YMMV, don't optimize prematurely, but don't also assume "re-render everything and the virtual DOM will always make it fast enough".
> If you're re-rendering a div in different places using Om/React in order to animate it, I would say you're probably doing it wrong. You should do one re-render to apply a new CSS transition class.
Unless, of course, that doesn't make sense, like if you're implementing drag and drop.
I wouldn't use React to render a dragged UI component, I would animate it in the DOM myself then send an action back to the store when it was dropped that triggered a re-render. Why would the application need to know where the UI component was before it was dropped?
My hn comment history has some discussion about it
I'm on my phone now but if you email me I can give more info. I've used react-cursor in two enterprise grade apps in regulated industries and couldn't imagine using react without cursors (believe me I tried)
Om is really cool and David Nolen is an awesome member of the Clojure community. However, I've played around with Om and found it to be a rather complex tool. I think it would be perfect for a high performance web app where I need to control everything - but thats not what I generally need.
I've enjoyed working with Reagent, which is another react clojurescript interface. It sacrifices some very minor functionality for an extreme decrease in complexity. It's actually enjoyable to make a UI with it.
I just glanced over the om-tools but I particularly like that "don't need to use apply", and I think the "no boilerplate code around using reify" from defcomponent is gonna be usefull to me too. The other features - gotta try them out.
+1 for Reagent. Best UI library that I've ever used. Om is really confusing to me and requires too much configuration to make it usable. Reagent works out of the box and just makes so much sense. It never feels like you're using a framework, it just feels like using a tool to abstract away the complexity of rendering from your UI logic. It's really enjoyable to use. Then again it hasn't been updated in six months so there's that...
Reagent is awesome and it has a growing community largely due to the fact that Om doesn't jive with some people. I certainly do not think Om is anywhere near the final answer for programming client side user interfaces with immutable values.
+1 for Reagent. A lot of curious HNers seem to find Om first, but Reagent is a very simple straightforward way to get going with React and Clojurescript.
It's nice to see the Clojure/ClojureScript community growing. But the fact remains that these tools still don't enjoy the maturity of their more mainstream counterparts.
So if you use any ClojureScript libraries in production, expect to run into strange issues and submit bug reports that are not responded to for months on end. Nothing against their authors, it's just the nature of writing niche tools.
This is not my experience. Doing JavaScript for years I mostly leaned on jQuery & Underscore.js and more recently React. ClojureScript has all the functionality of Underscore.js and a whole lot more, Google Closure I honestly find just as usable as jQuery for the cross browser bits, and finally there are several React bindings like Om. And I do a pretty darn good job of maintaining Om :)
There are still some huge hurdles to overcome for working on large projects & medium-size teams with designers. Nothing insurmountable, but a lot of polish that still needs to be put into (re-)usable infrastructure, tooling, and polish.
All that said, once devs are up to speed, and designers have a modicum of tooling, it's the best system I've used so far.
I think OP is talking about CLJS development at large, not Om specifically.
Personally, I ran into an issue with the Chas Emerick's testing framework and lost about a day of dev time trying to track down the issue. I never did figure out what was wrong and ended up just scrapping and starting over. In the end, it would have been nice to have some alternatives, but there really weren't any mature ones at the time.
Contrasting the situation with a language like Python where, should I run into an issue with Py.Test, I can always switch to Nose or numerous other frameworks.
Searching for documentation for CLJS is also a bit rough. I ended up relying on ClojureDocs for the most part, but because CLJS transpiles to JS and not JVM code, there are some undocumented idiosyncrasies. This also complicates troubleshooting because the errors are thrown by something like line 6400 of the transpiled code, not line 20 of the code you have been writing.
I still love the language. I am even taking part in a CLJ/CLJS specific hackathon next weekend. However, I have trouble recommending it to others without caveats. Most of which are due to the language's relative immaturity.
A very interesting point was the statement "...renders the MVC terminology obsolete. Not that we don’t have models, views and controllers anymore; it’s just that the layering of the three is no longer relevant."
Could someone please expand on this statement? If the purpose of the MVC separation was a separation of concerns and organization, what is the impact of their layering no longer being relevant?
I wouldn't choose the same words as the author. Perhaps I would say this instead: "... it’s just that the layering of the three is no longer pre-ordained by the framework."
Here's my take. Om's "application state" (only data) corresponds to the data part of the model ("M") in most MVC frameworks. In Om, just like ClojureScript or Clojure, you use functions, organized however you like, to work with data. This means that the "action-oriented" part of a traditional MVC model (e.g. methods in OO) are not necessarily grouped with the model data. Perhaps this is what the author is getting at: you can "layer" (organize) your functions into whatever namespaces you like.
In in my recent experience building Om apps, I've found that organization matters. As an Om app grows in size, it is natural (and important) to find ways of organizing that help. Om does not impose any particular organization scheme. I personally find ClojureScript namespaces to be very easy to work with; I simply split up a codebase as files become larger and harder to summarize.
"... it’s just that the layering of the three is no longer pre-ordained by the framework."
That is pretty close to what I think. And I don't have any great certainty about any of this. I'm not going to go around saying "MVC is dead!"
What I observe with Om is that Om works well because it determines a flow of data, and the tools that we have to influence that flow of data, which could be identified with M, V or C, don't need to have any kind of formal separation, as opposed to a framework where you would have Model objects, Controller objects and View objects. It seems to me that Om and React have shifted the metaphor a little bit, and that the MVC metaphor isn't the best one to describe what is going on.
26 comments
[ 2.5 ms ] story [ 72.5 ms ] threadI am still, however, a big fan of the immutable/Om way of doing things with React, and it will be the way I build future React projects from the ground up.
Some feedback on the article itself:
"For an animation, like moving a <div> from left to right, the IRenderState function might be called several times a second. Sometimes it is better to think of your app like a movie (a cartoon, I guess) rather than a static set of objects sitting around waiting for events to happen. And if you are thinking: “that must be horribly slow”… well, it isn’t, thanks to React’s virtual DOM that efficiently projects changes out into the real DOM."
If you're re-rendering a div in different places using Om/React in order to animate it, I would say you're probably doing it wrong. You should do one re-render to apply a new CSS transition class.
Also, lots of re-rendering on desktop Chrome is, usually, quite fast because of V8 and desktop CPU's... try doing this on mobile, or (in our case) a DTV set-top-box, and you'll quickly run into performance issues. Not every JavaScript engine/platform is fast enough to pull off the naive "re-render everything" approach in React. That's why they included shouldComponentUpdate, and why David Nolen went to such effort to facilitate cheap shouldComponentUpdate checks in Om.
Of course, YMMV, don't optimize prematurely, but don't also assume "re-render everything and the virtual DOM will always make it fast enough".
Unless, of course, that doesn't make sense, like if you're implementing drag and drop.
It supports o(1) shouldComponentUpdate
My hn comment history has some discussion about it
I'm on my phone now but if you email me I can give more info. I've used react-cursor in two enterprise grade apps in regulated industries and couldn't imagine using react without cursors (believe me I tried)
[0]: https://github.com/swannodette/om#applications-built-with-om
I've enjoyed working with Reagent, which is another react clojurescript interface. It sacrifices some very minor functionality for an extreme decrease in complexity. It's actually enjoyable to make a UI with it.
[0]: https://github.com/Prismatic/om-tools
So if you use any ClojureScript libraries in production, expect to run into strange issues and submit bug reports that are not responded to for months on end. Nothing against their authors, it's just the nature of writing niche tools.
All that said, once devs are up to speed, and designers have a modicum of tooling, it's the best system I've used so far.
Personally, I ran into an issue with the Chas Emerick's testing framework and lost about a day of dev time trying to track down the issue. I never did figure out what was wrong and ended up just scrapping and starting over. In the end, it would have been nice to have some alternatives, but there really weren't any mature ones at the time.
Contrasting the situation with a language like Python where, should I run into an issue with Py.Test, I can always switch to Nose or numerous other frameworks.
Searching for documentation for CLJS is also a bit rough. I ended up relying on ClojureDocs for the most part, but because CLJS transpiles to JS and not JVM code, there are some undocumented idiosyncrasies. This also complicates troubleshooting because the errors are thrown by something like line 6400 of the transpiled code, not line 20 of the code you have been writing.
I still love the language. I am even taking part in a CLJ/CLJS specific hackathon next weekend. However, I have trouble recommending it to others without caveats. Most of which are due to the language's relative immaturity.
Once you have these working, errors are linked to line 20 of hte code you are writing.
Could someone please expand on this statement? If the purpose of the MVC separation was a separation of concerns and organization, what is the impact of their layering no longer being relevant?
Here's my take. Om's "application state" (only data) corresponds to the data part of the model ("M") in most MVC frameworks. In Om, just like ClojureScript or Clojure, you use functions, organized however you like, to work with data. This means that the "action-oriented" part of a traditional MVC model (e.g. methods in OO) are not necessarily grouped with the model data. Perhaps this is what the author is getting at: you can "layer" (organize) your functions into whatever namespaces you like.
In in my recent experience building Om apps, I've found that organization matters. As an Om app grows in size, it is natural (and important) to find ways of organizing that help. Om does not impose any particular organization scheme. I personally find ClojureScript namespaces to be very easy to work with; I simply split up a codebase as files become larger and harder to summarize.
"... it’s just that the layering of the three is no longer pre-ordained by the framework."
That is pretty close to what I think. And I don't have any great certainty about any of this. I'm not going to go around saying "MVC is dead!"
What I observe with Om is that Om works well because it determines a flow of data, and the tools that we have to influence that flow of data, which could be identified with M, V or C, don't need to have any kind of formal separation, as opposed to a framework where you would have Model objects, Controller objects and View objects. It seems to me that Om and React have shifted the metaphor a little bit, and that the MVC metaphor isn't the best one to describe what is going on.