Fork me on GitHub
#reagent
<
2022-08-03
>
Pepijn de Vos13:08:42

How can I have an input field with an onchange handle that has a debounce attached, but still has the value of the atom it's synchronized to? Basically if I use :value it doesn't let you change it at all, and if you use :default-value it doesn't change when the atom changes.

Pepijn de Vos13:08:54

:default-value (:decltempl @mod) ; doesn't update the input when @mod changes
:value (:decltempl @mod) ; doesn't let me change the input
:on-change (cm/debounce #(swap! mod assoc :decltempl (.. % -target -value)))

Pepijn de Vos13:08:35

(`(def debounce #(goog.functions/debounce % 1000))` )

p-himik15:08:23

Wrap :input with your own component, have two ratoms within it that track the internal value and the external value. Update the internal one in the :on-change and when the passed external value does not correspond to the stored external value on render. Call the debounced function in :on-change as well. You can see how re-com does it in its input components.

Pepijn de Vos15:08:03

You lost me on the external value corresponding render part

Pepijn de Vos15:08:21

What is this re-com we're talking about?

p-himik15:08:11

Should be quite easy to find on the web. ;)

p-himik15:08:28

A UI library from the guys behind re-frame.

Pepijn de Vos15:08:49

But just the exact thing I'm not understanding is not clear to me from this code either... when is this external model compared? I just can't figure out where this is going with the deep nesting going on https://github.com/day8/re-com/blob/fcebaeefbe87c664222d50b69bdfc1b3faa05db8/src/re_com/input_text.cljs#L97-L99

p-himik15:08:31

It's a form-2 Reagent component. That comparison is done on every call to that component, which happens every time its properties change (with some exceptions that aren't of any importance here).

Pepijn de Vos15:08:01

Ah so simplified version would be someting like

(defn outer [extern]
  (let [ext (r/atom) int (r/atom)]
    (defn [extern]
      (when (not= @extern @ext)
        (reset! ext @extern)
        (reset! int @extern))
...
and then you debounce both the external states, and update the internal one directly?

p-himik15:08:20

Only the inner defn is actually fn, right. No, you debounce only the :on-change you pass to the component. Don't debounce state comparison and resetting - just reuse what re-com has. Should work.

Pepijn de Vos15:08:09

yea but on-change needs to update all 3 values right

Pepijn de Vos15:08:57

interal for rendering, and debounced both external values, to make the change visible, and to be able to detect external changes

p-himik15:08:42

There are two :on-change functions - one your component receives and one the wrapped :input receives. The former should be debounced, called from the latter, and it should change the external model. The second :on-change should call the first one and change the internal model. Well, I'm just describing what should be going on based on my memory of the approach. The actual working source code of re-com should be a better reference than that, even if it doesn't have any debouncing in there (which you can add by just wrapping a not debounced re-com component).

Pepijn de Vos09:08:03

Hm why not use track! to update the internal state when the external changes, instead of having two copies of the external state? I guess problem is where do you call dispose! to avoid leaking the component...

Pepijn de Vos10:08:42

For future reference:

(defn dbfield [typ props st valfn changefn]
  (let [int (r/atom @st)
        ext (r/atom @st)
        dbfn (debounce changefn)]
    (fn [typ props st valfn changefn]
      (when (not= @ext @st)
        (reset! int @st)
        (reset! ext @st))
      [typ (assoc props
                  :value (valfn @int)
                  :on-change (fn [e]
                               (let [val (.. e -target -value)]
                                 (dbfn st val)
                                 (changefn int val))))])))

StevenGL20:08:39

Hello, newbie question. I've got svg file and would like to render it as a component so I can apply styles to it. What would be the easiest way?

StevenGL21:08:53

I'll check it, thanks

StevenGL20:08:12

@U2FRKM4TW your example code looks nice but I think I cannot apply it since my app is built with shadow-cljs

p-himik20:08:48

Why is the build tool important here? My code does not rely on any specific build tool and shadow-cljs doesn't have any issues with macros. In fact, I use shadow-cljs myself.

StevenGL16:08:43

you're right, I've got it mixed