freactive
freactive copied to clipboard
Question: are cursors more update-efficient than a massive atomic hash-map?
I often tend to put my application state in an all-encompassing one-or-two-level-deep hash-map wrapped in an atom. Let's say that a DOM element :div#a
reactively (i.e., via the rx
macro) changes its color based on (-> @state :div#b :style :color)
. Now let's also say that :div#c
reactively changes its color based on (-> @state :div#d :style :color)
. Div A depends on div B, and C on D, but [A and B] do not affect [C and D] at all, and vice versa.
Now suppose that (swap! state assoc-in [:div#b :style :color] :white)
is called. Because the value of state
is atomically modified, are the colors of divs A and C considered "dirty" and thus the values must be recalculated by calling the functions which rx
created for each of them respectively? Or does freactive know that only div B's color was changed and so C's does not need to be recomputed?
This brings me to the question in the title: would cursors be more efficient in this case? Instead of writing code like so:
[:div#a {:style {:color (rx (-> @state :div#b :style :color))}}]
...should I write code like this?:
[:div#a {:style {:color (let [c (cursor state (comp :color :style :div#b)]
(rx @c)
That is, would cursor c
only be invalidated if div B's color changed and not on every modification to state
?
Thanks for your time!
Yes, cursors should be more efficient if used correctly. Especially the new cursor implementation.
You can write (cursor state [:div#b :style :color])
instead of the comp
if you like.
Although, it's not documented yet (haven't had the time) there is a fn in freactive.core assoc-in!
that would be more efficient than (swap! state assoc-in ...)
. The functions designed for transients assoc!
and dissoc!
as well as freactive.core's update!
, assoc-in!
and update-in!
all work with cursors and eliminate unnecessary notifications - this is the most efficient way. Using swap!
+ cursors naively should have some benefits over naive rx's too, but only at deeper levels of nesting.
Also, (rx @c)
is redundant. Just bind c
directly as in [:div c]
.
Wow, thanks so much for the impressively quick reply! I really appreciate the helpful hints - I'll make good use of them.
Also, I really have so much respect for what you've accomplished in this library - about how easy-to-use and well-reasoned it all is, and about how you respond so quickly to new issues, etc. If you don't mind me asking, is this a side project, part of a master's project, part of a work assignment - how did this come about?
Mostly to support commercial projects. I hacked together freactive.dom initially from frustrations with om and reagent not working quite as smoothly as I had hoped. Currently trying to put a commercial app into production with this.
On Fri, Jun 12, 2015 at 6:52 PM, alexandergunnarson < [email protected]> wrote:
Wow, thanks so much for the impressively quick reply! I really appreciate the helpful hints - I'll make good use of them.
Also, I really have so much respect for what you've accomplished in this library - about how easy-to-use and well-reasoned it all is, and about how you respond so quickly to new issues, etc. If you don't mind me asking, is this a side project, part of a master's project, part of a work assignment
- how did this come about?
— Reply to this email directly or view it on GitHub https://github.com/aaronc/freactive/issues/47#issuecomment-111635677.
Yeah, I had frustrations with reagent, even though it really is the simplest of all reactive ClojureScript UI libraries that I know of (besides freactive), and I never even tried om because I took one look at it and it just shouted "incidental complexity". I'm also trying to put a commercial app into production with this, but it may take a while because I'm the sole developer at the moment.
Good luck! Curious - are there particular frustrations you had with reagent that you feel are solved with freactive?
On Fri, Jun 12, 2015 at 7:07 PM, alexandergunnarson < [email protected]> wrote:
Yeah, I had frustrations with reagent, even though it really is the simplest of all reactive ClojureScript UI libraries that I know of (besides freactive), and I never even tried om because I took one look at it and it just shouted "incidental complexity". I'm also trying to put a commercial app into production with this, but it may take a while because I'm the sole developer at the moment.
— Reply to this email directly or view it on GitHub https://github.com/aaronc/freactive/issues/47#issuecomment-111637568.
Can you explain why (rx @c) is redundant
in this case?
It's not only redundant, it adds overhead. Any IWatchable is bindable directly. On Fri, Jun 19, 2015 at 4:26 PM pkobrien [email protected] wrote:
Can you explain why (rx @c) is redundant in this case?
— Reply to this email directly or view it on GitHub https://github.com/aaronc/freactive/issues/47#issuecomment-113632227.
In my experiments I'm having trouble with cursors that aren't "leaf nodes" or that point to something like a js/Date. object. The code in question is here: https://github.com/pkobrien/ing/blob/93aeda2b2fb6c89a5ba214ad4b7e38c5d2af485b/styling/src/app/core.cljs
Here is a simplified portion:
(defonce app-state
(r/atom
{:app-name "Styling"
:mouse-pos {:x nil
:y nil}
}))
(defonce rc-mouse-pos
(r/cursor app-state :mouse-pos))
(defonce rc-mouse-pos-x
(r/cursor app-state [:mouse-pos :x]))
(defonce rc-mouse-pos-y
(r/cursor app-state [:mouse-pos :y]))
(defn- listen-to-mousemove! []
(dom/listen!
js/window "mousemove"
(fn [e]
(assoc! rc-mouse-pos :x (.-clientX e) :y (.-clientY e)))))
(defn app-html []
[:div
[:p "Mouse position: " (rx (str "(" (:x @rc-mouse-pos) ", " (:y @rc-mouse-pos) ")"))]
[:p "Mouse position: " (str "(" (:x @rc-mouse-pos) ", " (:y @rc-mouse-pos) ")")] ; NOT working
[:p "Mouse position: " (str "(" (:x rc-mouse-pos) ", " (:y rc-mouse-pos) ")")] ; NOT working
[:p "Mouse pos: " (str (vals @rc-mouse-pos))] ; NOT working
[:p "Mouse X: " rc-mouse-pos-x]
[:p "Mouse Y: " rc-mouse-pos-y]
])
(dom/mount! "app" (app-html))
Based on this example it looks like rc-mouse-pos-x and rc-mouse-pos-y (leaf nodes) can be bound directly, but rc-mouse-pos (interior node) cannot.
Yeah so, in order to bind a ref it has to be a direct child of an element. (str (vals @rc-mouse-pos))
for example just deref's rc-mouse-pos once and statically inserts the value in the element - there's no reactive binding. You need something like (rx (str (vals @rc-mouse-pos)))
. It's the same in reagent for example, it's just that everything in reagent is captured under big component bindings with the [my-component-fn ...]
syntax (also supported by freactive but not really recommended). freactive is encourages being more specific and as a result can be more efficient... but it might also be a little more typing. For me, I like the simplicity of direct binding and it allows for more complex performance scenarios - in simple cases you probably won't notice much difference.