Fork me on GitHub

Hi, am I right re-frame doesn't play well with reagent cursors? I can create a cursor but when I update it using reset! UI doesn't redraw. Should I use dispatch instead?


hm, sorry. seems to work fine. I was creating cursor on resolved value @

(let [v @x
      y (r/cursor v [])] ...(reset! @y 42))


I don't understand how does this works and which is preferred: using handlers and dispatch or cursors and reset! ?


It would be unusual to use cursors with re-frame. Very unusual.


That was me being diplomatic. What I really wanted to say was that cursors are awful, and a horrible blight and that you should never use them (outside of toy applications) and absolutely never with re-frame. Ever.


BTW, your code reset! @y 42 should be reset! y 42. But now I just feel dirty for having facilitated cursor use.


@mikethompson: what about using cursor for local state, e.g. input form state?


For local state ratoms are good enough, I think


Lookin at re-com accepting atoms as :model i thought that's the intended scenario. You have one ratom with form state and a lot of inputs. You pass cursor for specific value to :model and don't bother about updating the ratom object.


@mikethompson: Thanks, I'll change to dispatch


@nidu re-com doesn't use cursors


The API of various components use a pattern where ratoms are used to pass information "in" and callbacks are used to communicate "out" changes of state


Sorry then, my misunderstanding.


If you think about it, the re-com components are using a variation of what re-frame does


A "reactive" source of data. And sending events for handling.


That approach was very deliberate.


But absolutely no use of cursors.


Got it, thanks. It seems i confused reagent cursors with om ones.


Can someone help clarify why it’s incorrect to deref a subscription in the let block of the outer function of a component, versus deref’ing inside the inner render function?

(defn component []
  (let [my-val (deref (subscribe [:value])
    (fn []
      [:div my-val])))
If you do this, say, for an input :type “text” only the last keystroke will register and render into the input box on change. The outer function is called once for the component, and the subscribe is registered to re-run the inner render function any time its input signal changes. For “convenience” I wanted to try to deref in the let block, so I don’t have to do something like …
[:div @my-val
  [.p @my-val]
  [.p (count @my-val]]
All over the render fn when I have multiple uses of the ratom. I see why it’s wrong, but I dont fully understand why it’s wrong. The inner render is wrapped in a reaction to produce a new signal any time a deref’d ratom changes… so because there are no @ inside the render, the reaction never triggers. The outer block is invoked once, and if you deref a ratom immediately… ???


I don’t see why a input :type “text” :value my-val psuedo “works”, always updating to display the last character input, but never remembering previous… should it not always display “”, or the default value provided, and never change?


> Can someone help clarify why it’s incorrect to deref a subscription in the let block of the outer function of a component, versus deref’ing inside the inner render function? From what you write afterwards, I think you do understand ... but I'll repeat it back to you ... The outer function is run once. The inner render is run every time one of its "inputs" changes (inputs can be either props or ratoms). So if you subscribe in the outer and then immediately deref you are obtaining the initial *value* of the subscription. But then, because outer is only ever run once, that's it - that value will never change. That value remains the initial value of the subscription forever. There's obviously a difference between a "value" and a "ratom-containing-a-value". The subscription is delivering "ratom-containing-value" (which is a Signal) and, by derefing it, you are extracting the immutable value within it which is, of course, not a Signal. An inner render will respond to changes in a "ratom-contain-value" by rerendering, but a value can't ever change. Perhaps you'd feel better about this kind of an arrangement ...

(defn component []
  (let [my-val (subscribe [:value])]
    (fn []
      (let [v  @my-val]            ;; <--- you can do this INSIDE the inner
        [:div  v
          [:p v]
          [:p v]])))


Or alternatively ... you can do it via two components, a parent one which sources the data and and child one which does the rendering.

(defn child 
  [:div  v
    [:p v]
    [:p v]])

(defn parent
  (let [my-val (subscribe [:value])]
    (fn []
       [child @my-val])))       ;;  derefed 


Regarding your misbehaving input, you might need to post a gist. Not enough information given to explain why you see the particular behavior you do.


BTW, although you do appear to have "got it", something is still obviously puzzling you, so you might still want to browse back over:


hey many thanks @mikethompson, you’ve been a big help. Also, I’m growing to appreciate the simplicity reframe brings to overblown and imposing architectures. Job well done I’d say.


@lwhorton: kind of you to say, thanks. We like it too simple_smile


@mikethompson: have you thought about making your with subs macro a more exposed part of re-frame? I thought that was a really good idea and it solves the idea you just described above while taking care of the possible memory leak


you could even take it one step further and make a defui macro that has 3 different airities. [name child] -> turns into a simple type one component which takes no props and just renders the child [name props child] -> also a type one component which takes props and renders the child [name props subs child] -> creates a type two component which does essentially what with-subs does


I just threw this together in like two seconds, but maybe something like this?