Fork me on GitHub
#re-frame
<
2017-07-15
>
Célio19:07:39

Hi, I've been banging my head against the wall trying to get an input's atom updated when a subscription changes. Has anyone tried that before?

Célio19:07:51

I have something along these lines:

(defn foo []
  (let [bar (rf/subscribe [:bar])
        value (r/atom @bar)]
    (fn []
      [:input {:value @value
               :on-change ...
I understand in the code above why value doesn't get updated when the subscription is updated. I just can't figure out a way to accomplish what I need: to update value when bar is updated.

metametadata19:07:46

@ccidral you could try using r/run!:

(defn foo []
  (let [bar (rf/subscribe [:bar])
        value (r/atom nil)]
    (r/run! (reset! value @bar))
    (fn []
      [:input {:value @value
               :on-change ...

metametadata19:07:31

it requires (:require-macros [reagent.ratom :refer [run!]])

metametadata19:07:55

edited: removed initial value in atom, because run! is executed immediately

Célio19:07:40

holy cow thanks @metametadata it works like a charm

Célio19:07:12

any caveats that I should be aware of?

metametadata19:07:42

patterns like this (adding watches to stuff) are prone to memory leaks because it's easy to create circular references

metametadata19:07:25

but it's hard to predict for me if this particular one has this problem

Célio19:07:45

got it, I'll watch my step

Célio19:07:26

is it right to say foo is not a pure function anymore? and, should we care?

metametadata19:07:29

well, React components are stateful and are free to manage their own state, so it's looks ok to me

Célio19:07:10

my thoughts exactly

metametadata19:07:00

maybe I would think about moving this "last change wins" logic into the event handler instead of keeping it in the component, if possible

metametadata19:07:46

because it looks a bit unusual

metametadata19:07:40

but I can't tell exactly why I don't like it 🙂

Célio19:07:00

in my case bar is updated by an event handler, and I have to update the input's value with bar's value

Célio19:07:15

well of course any app state change is carried out by an event handler 😛

Célio19:07:19

my first attempt was to add a watch to bar that updates value but it didn't work

metametadata19:07:38

I see. What I meant was, unless there's some additional trickery in the component, ideally ratom is not need and component can only depend on the sub:

(defn foo []
  (let [bar (rf/subscribe [:bar])]
    (fn []
      [:input {:value @bar
                   :on-change (event-that-changes-bar)

Célio19:07:12

oh I see ... in this particular case bar should change only in a particular moment (ie: not immediately as the user changes the input's value)

Célio20:07:36

it would be nice if there was a function that binds a subscription to a ratom and propagates changes from the former to the latter

Célio20:07:24

something like (bind bar value)

Célio20:07:45

This also works:

(defn bind [src target]
  (add-watch src
             :rf-binding
             (fn [_ _ _ value]
               (reset! target value))))
Then
(bind bar value)

Célio20:07:35

I tried add-watch before but didn't work probably because I did something wrong

pesterhazy21:07:43

there's a klipse live demo if you scroll to the bottom

pesterhazy21:07:59

there's also r/track! for adding side effects to state changes