I've done some research too - check out this implementation of cursors, it enables drop-in O(1) shouldComponentUpdate (like Om) and is compatible with rAF batching (like Om). We explored Mori for a bit too and ended up deciding Mori wasn't worth it. (Our app was big enough that we experienced pretty brutal performance without shouldComponentUpdate. Now our bottleneck is methods like Array.prototype.map over large lists in render, which Mori can't help with - we have to restructure our state away from lists and into trees to better take advantage of shouldComponentUpdate)
(It is backed by react.addons.update, it provides a mechanism like mori.assoc_in for immutable subtree updates, and it preserves reference equality for equivalent cursors (value/onChange tuples))
Very cool! I had researched this technique before, when I found cortex: https://github.com/mquan/cortex. Cortex does something very similar, except it doesn't actually do persistent data structures. Building cursors off of the addons.update stuff is a neat idea. (why `onChange` instead of `set` and `pendingValue` instead of just `value`?)
I agree that you can take this really far. At this point I need to sit back and thing about it. :) I think mori could provide better performance for certain types of apps, but it does come at a cost if interop. Time to hit the hammock.
Hm, Cortex looks almost like my Avers (https://github.com/wereHamster/avers). With the major difference that Avers uses Object.observe instead of explicit setters/getters, and Avers mutates data in-place. I also use it with react, and haven't noticed any performance issues. But I have an idea how to make it work with immutable data (to allow === comparison in shouldComponentUpdate).
We chose cursor.value and cursor.onChange to line up directly with react's value/onChange convention. But, we are considering a nomenclanture change and we will probably expose all the react.addons.update - set, merge, push etc. Knowing when to choose value vs pendingValue() is essential complexity, but cursors make it a mechanical decision - in a lifecycle method? use `value`, in an event handler? use `pendingValue()`.
I know you've probably answered this question a million times... but could you briefly explain your enthusiasm for clojurescript and/or om? I've been using the react js library and have been wondering what makes clojurescript a step above javascript and om a step above react.
https://github.com/dustingetz/react-cursor/blob/master/examp... https://github.com/dustingetz/react-cursor/blob/master/js/Cu...
(It is backed by react.addons.update, it provides a mechanism like mori.assoc_in for immutable subtree updates, and it preserves reference equality for equivalent cursors (value/onChange tuples))
(Cursors are also not vulnerable to issue#122 https://github.com/facebook/react/issues/122)