Fork me on GitHub
#reagent
<
2018-07-20
>
jsa-aerial00:07:25

Is this correct: for a form 3 component, what may have been done in the now deprecated :component-will-update should now be done in :component-did-update?

jsa-aerial00:07:33

Actually, I'm now thinking I don't want/need to do either of these. The situation is I'm painting new stuff on a canvas based on updates from a server that is pushing the data. Currently the idea was that the 'content' was rebuilt in the :component-will-update function, but seems like this can be rebuilt in the message handler and things will just work. Yes/no?

justinlee00:07:08

If it can be done in component did mount do it there. That’s always the most stable place.

justinlee00:07:07

Similarly in component did update

justinlee00:07:11

Doing it in the message handler might be annoying because it might split the logic up. Hard to say without seeing. I guess if it is incremental and it’s a canvas that doesn’t resize that might work.

jsa-aerial15:07:40

@lee.justin.m Thanks for you input on this already. But I am scratching my head and I think you pretty savvy about react stuff, so let me ask another question - it may be just the question "am I totally lost?"

jsa-aerial16:07:10

Anyway, I could not get the new canvas updates to draw unless I used component-will-update. Nothing else was working when using a form 3. But I kept whittling it down by trying various stuff. Also tried using Reacts so called 'totally uncontrolled component with keys' - never could get that to render anything. So, I did punt out to - when msg comes in from server, repaint canvas outside of component and then just rerender. This always seems to work - I was able to remove all but the render function from the component. Which I think means I could simply use a form 1 or maybe form 2 component.

jsa-aerial16:07:17

But, the canvas paint is outside the react/reagent flow. Is this typically considered "bad"?? Thanks for your input!

jsa-aerial16:07:20

Oh, one other thing that seems nicer this way is that I no longer get any 'flicker'. Previously when painting inside did mount/will update, the graphic always flickered - like it repainted twice. Now that never happens

justinlee17:07:50

in my canvas, the render func just returns a target div (you could directly return the canvas element, but i’m using a library that creates the canvas for me). i use a ref callback to get a handle to the node. then, in component-did-mount and component-did-update (both of which fire after render), i just call the canvas-drawing code (same function for both).

justinlee17:07:48

I’m not sure what’s causing the flickering--it is surprising to me that putting it in the render func fixed that becasue will-update/render/did-update all fire together in sequence

justinlee17:07:44

one other thing to note: depending on what you are passing in, you may well need to implement should-component-update to avoid unnecessary re-renders. my canvas is the only place in my code where i’ve needed that

justinlee17:07:56

I’m not quite sure what the real issue is here. Clearly you’re not getting your lifecycle methods hooked up. In terms of whether it is “bad” to be outside the react/reagent flow, it kind of depends. Canvas painting is always outside of the flow in some sense. It is cleaner, of course, to have all of the code related to the canvas in one place instead of having it split between some message handler code and some reagent code. but don’t let perfect be the enemy of good

jsa-aerial17:07:26

Thanks. I think I'm still confused on several things with what is going on. In answer to your suspicion about this 'working in the render fn' I must have misled you there - it definitely does not work there. It was 'working' when the draw was outside altogether - in the msg handler. But I notice now that I was confused about what was happening. Once the div (holding the canvas) is rendered the drawing on canvas in the msg handler was the thing 'updating' the element - the reagent/react component was no longer even being called. So, that looks like a bad way to go.

jsa-aerial17:07:19

I agree that I am clearly messing up the lifecycle stuff - well if I use the did-mount with will-update, then things do seem to work the way that flow was intended, but since will-update is going away and deprecated, I would prefer to move away from that.

jsa-aerial17:07:08

I'm using a lib that creates the canvas as well. So, whatever you are doing probably is the right way to go. What is the ref callback you mention - or is that part of how the lib you are using works? @lee.justin.m Thanks again!

justinlee17:07:50

so in your render, you’ll need to get a ref to the div node so that you can pass it to the library

jsa-aerial17:07:52

A bit more: when trying to use did-mount and did-update, both have this body: (visualize new-spec (rgt/dom-node comp))

justinlee17:07:53

[:div {:ref #(swap! state assoc :div-node %)}]

justinlee17:07:07

^ that will put the target node in the atom

jsa-aerial17:07:22

It works the first time (probably the did-mount), but after nothing ever updates

justinlee17:07:44

put logging statement everywhere so you can see what the hell is going on

justinlee17:07:07

put it in the outer closure, in the render, and in all the lifecycles

jsa-aerial17:07:26

Yeah, I have logging all over and that's how I found out I was lost. That :ref idea could be just the trick!

justinlee17:07:09

oh, so when your message handler receives data from the server, is it passing it down as props? you have to do something to actually trigger the react lifecycle remember

jsa-aerial17:07:47

I'm sure that is where I'm getting confused - the msg handler (when not doing the canvas writing itself...) was updating the ratom which the visualize component was derefing.

justinlee17:07:24

right so that’s not going to work because you aren’t dereffing the ratom in the render function

justinlee17:07:56

the way i solve this problem is to wrap: a dumb outer component derefs the data and passes everything as props to the inner component

justinlee17:07:07

(make it easier to test, too)

jsa-aerial17:07:43

Hmmmm, let me give you what the main bits of this are. I'm not sure I can make a code fragment in a thread, but I'll try:

jsa-aerial17:07:59

(defn vgl
  "Reagent component to render vega/vega-lite visualizations."
  [spec]
  (rgt/create-class
   {:display-name "VGL"

    :component-did-mount
    (fn [comp] (visualize spec (rgt/dom-node comp)))

    :component-did-update
    (fn [comp [_ new-spec]] (visualize new-spec (rgt/dom-node comp)))

    :reagent-render (fn [spec] [:div#app #_{:key (next-key)}])}))

(defn hanami [ws]
  (when-let [spec (get-adb [ws :cur-vgl])] [vgl spec]))

(when-let [elem (js/document.getElementById "app")]
  (printchan "Element 'app' available")
  (rgt/render [hanami :foo] elem))

jsa-aerial17:07:08

visualize is the thing that uses the lib that creates/updates canvas

jsa-aerial17:07:53

get-adb derefs the ratom involved. So, upon the first update of that ratom everything works the way I would hope. Subsequent updates with new specs - nothing happens. But if I change did-update to will-update, everything just works

justinlee18:07:53

did-update takes [this old-argv]. you need to access the current props by calling (reagent/props this)

justinlee18:07:20

it works when you change it to will-update because will-updates takes the new argv

justinlee18:07:18

@U06C63VL4 ^^ not sure if you saw this. i was busy chatting elsewehre for a while

jsa-aerial18:07:14

Hi, been messing with it using that last info. Something has indeed changed to make it start 'working'. But I'm still confused about what the hell is going on 🙂 Here's latest

jsa-aerial18:07:39

(defn vgl
  "Reagent component to render vega/vega-lite visualizations."
  [spec]
  (printchan "VGL called")
  (rgt/create-class
   {:display-name "VGL"

    :component-did-mount
    (fn [comp] (visualize spec (rgt/dom-node comp)))

    :component-did-update
    (fn [comp [preprop prestate snapshot]]
      (printchan (rgt/props comp) " snap: " snapshot)
      (printchan "PreProps " preprop " PreState" prestate)
      (visualize prestate (rgt/dom-node comp)))

    :reagent-render (fn [spec] [:div#app #_{:key (next-key)}])}))

justinlee18:07:10

that looks rightish 🙂

jsa-aerial18:07:57

OK, so, the print of (rgt/props comp) and snapshot just prints snap: - nothing before or after that I can see. The print of preprops prints a function with args (props, context, updater) and prestate is [object, object]

justinlee18:07:51

that doesn’t make any sense 🙂

jsa-aerial18:07:03

What is weird to me is that prestate here should be the same positional arg as I had before, but now it works. So, does calling (rgt/props comp) somehow magically change it?

jsa-aerial18:07:36

let me just try that

justinlee18:07:09

I believe that did-mount’s signature is [this argv]. the first argument of argv is … this

justinlee18:07:11

i ignored the argv and I just call (reagent/props this)

jsa-aerial18:07:27

OK, this still works:

jsa-aerial18:07:41

:component-did-update
    (fn [comp [preprop prestate snapshot]]
      #_(printchan (rgt/props comp) " snap: " snapshot)
      #_(printchan "PreProps " preprop " PreState" prestate)
      (visualize prestate (rgt/dom-node comp)))

jsa-aerial18:07:50

Previously (using the signature of will-update) I had [comp [ new-spec]] for the signature. That is definitely wrong, but the position of prestate in the new signature matches the old newspec. I wasn't getting any errors - as in wrong signature when using that 'bad signature' for did-update, just nothing happening.

jsa-aerial18:07:08

So, it looks like preprop is some function (?!) prestate is actually the new state (!?!) and snapshot is some unknown [object object] (???). None of this seems to match what the react docs say

justinlee18:07:46

reagent changes the signtures

justinlee18:07:59

look at the docstring for create-class

justinlee18:07:17

it’s trying to maintain the props->argv mapping it does

justinlee18:07:39

yea it’s pretty annoying 🙂

jsa-aerial18:07:21

So, what is you idea of what (rgt/props this) is supposed to do/return?? (here I'm using comp for this). It just shows as nothing in the print - not undefined or null or nil - just nothing at all

justinlee18:07:03

for me it returns a props map

justinlee18:07:09

let me roll a quick test

jsa-aerial18:07:16

Maybe I'm just not including any such thing (by random chance...)

jsa-aerial18:07:58

I see that reagent claims the sig for did-update is [this old-argv] but the stuff passed is actually the new state

justinlee19:07:33

that’s confusing. in a sense it is the old argv because it’s already been updated

jsa-aerial19:07:49

Nothing like getting all twisted up - I was just plain wrong about this working - chasing a ghost... The reason why I thought it worked was I had inadvertently left the canvas paint in the data update handler (outside of reagent/react). When I get rid of that, and keep the new correct sig for did-update, all the prints come out, but nothing is rendered... Ugh. Frustrating...

justinlee19:07:10

okay well that’s actually progress 🙂

jsa-aerial19:07:44

Actually, I changed the print of prestate so that I call (js->clj prestate) before printing - and ..... (drum rolll) it is indeed the old state. Which is why nothing happens it just redraws the same thing

jsa-aerial19:07:23

That actually makes sense since did-update is called after renderer

jsa-aerial19:07:33

at least according to react

jsa-aerial19:07:44

You know, I don't understand why will-update has been junked - with the new flow they have I don't see anyway to make this work within their flows. The so called 'completely uncontrolled component' looked like a mechanism but you can change the keys all you want on the div and nothing updates.

justinlee19:07:01

oh sorry i led you astray: i forgot a tidbit about r/props: “props” are only set if the first argument to the component is a map

justinlee19:07:54

this works in my thing because i pass a big map as the single argument to my component

jsa-aerial19:07:18

OK .... I'm lost - would that be the 'spec' argument in my vgl component?

jsa-aerial19:07:17

That 'spec' arg is a map, but it is the actual vgl-spec - not some map containing the vgl spec

justinlee19:07:13

this is bonkers. you’re right. did-update is passing the OLD argv

justinlee19:07:20

like, before the update

justinlee19:07:22

that makes no sense

justinlee19:07:22

GAH. but r/props gives you the NEW state if you pass it properly

jsa-aerial19:07:56

well - react docs specifically says that it is the OLD argv

jsa-aerial19:07:16

which, as you say, is crazy and make did-update basically worthless

jsa-aerial19:07:00

What do you pass as that map? Something with a props key and state key or ??

justinlee19:07:03

that demonstrates the cray

justinlee19:07:16

also shows how if you pass a map as the first arg, it becomes the “props”

jsa-aerial19:07:10

Is the :val some kind of magic dust??

justinlee19:07:48

just wanted to pass a map

justinlee19:07:12

see #reagent — componentDidMount is supposed to receive previous props per the react docs

justinlee19:07:27

you need to use r/props and r/children

justinlee19:07:29

there is also r/argv which is probably what you want: see the updated gist https://gist.github.com/jmlsf/7fe736c8b6d7f236361aaad59a1113fd

jsa-aerial20:07:56

I'll take a look at that, but for the moment this works (though I have no idea what the hell is going on):

jsa-aerial20:07:29

(defn vgl
  "Reagent component to render vega/vega-lite visualizations."
  [spec]
  (printchan "VGL called")
  (rgt/create-class
   {:display-name "VGL"

    :component-did-mount
    (fn [comp] (visualize (:val spec) (rgt/dom-node comp)))

    :component-did-update
    (fn [comp [preprop prestate snapshot]]
      (printchan "Did-Update: called")
      #_(printchan (rgt/props comp) #_" / " #_(rgt/children comp))
      #_(printchan #_"PreProps " #_preprop " PreState" (js->clj prestate))
      (visualize (:val (rgt/props comp)) (rgt/dom-node comp)))

    :reagent-render (fn [spec] [:div#app #_{:key (next-key)}])}))

(defn hanami [ws]
  (when-let [spec (get-adb [ws :cur-vgl])]
    #_(visualize spec (js/document.getElementById "app"))
    (printchan "Hanami called with " ws)
    [vgl {:val spec}]))

(rgt/render [hanami :foo] elem)

justinlee20:07:30

okay so you can just get rid of the {:val ..} wrapper and just call (first (rgt/children comp)) instead of (:val (rgt/props comp))

justinlee20:07:46

the basic rule is: each positional argument to your component gets pass as a child unless the first positional argument is a map, in which case that one argument is passed as props and all subsequent args are passed as children

jsa-aerial20:07:51

Holy shit - so, r/argv can obtain the current argument vector from the this arg of the dispatch and the actual dispatch is passed the prev-argv. That is among the most idiotic structuring ever. On top of that, it looks like r/props returns the second element of the current argv and r/children returns basically (rest argv).

justinlee20:07:30

not quite: r/children returns (rest argv) only if the first argument is a map

jsa-aerial20:07:58

That rule makes absolutely no sense - and worse yet, I don't see anything like that documented anywhere

jsa-aerial20:07:17

What possible "rationale" is behind this crazy idea

justinlee20:07:16

everything about this so that you can write simple functions and pass positional parameters

justinlee20:07:55

well I think. the guy who made this mess disappeared off the map in 2016, so it is mostly juho who does maintenance and a few people who hang out in #reagent and keep the wheels from coming off the bus

jsa-aerial20:07:24

I don't see how that explains it - you could just do that with ONE consistent and documented scheme and be done with ti

justinlee20:07:47

but if you look at the change history, dan didn’t want to write (defn my-comp [props children]) he wanted to write (defn my-comp [arg1 arg2 arg3])

justinlee20:07:15

the interface is not good, i’m just trying tell you how I think we got here

jsa-aerial20:07:50

✔️ understood - I know you didn't have anything to do with it as I believe you are relatively new to it (from JS/React land)

jsa-aerial20:07:56

OK, this works and is based on your @lee.justin.m last suggestion:

jsa-aerial20:07:19

(defn vgl
  "Reagent component to render vega/vega-lite visualizations."
  [spec]
  (printchan "VGL called")
  (rgt/create-class
   {:display-name "VGL"

    :component-did-mount
    (fn [comp] (visualize spec (rgt/dom-node comp)))

    :component-did-update
    (fn [comp old-useless-argv]
      (printchan "Did-Update: called")
      (let [new-useful-argv (rgt/children comp)
            new-spec (first new-useful-argv)]
        (printchan "NEWARGV " new-useful-argv)
        (visualize new-spec (rgt/dom-node comp))))

    :reagent-render (fn [spec] [:div#app #_{:key (next-key)}])}))

(defn hanami [ws]
  (when-let [spec (get-adb [ws :cur-vgl])]
    #_(visualize spec (js/document.getElementById "app"))
    (printchan "Hanami called with " ws)
    [vgl spec]))

justinlee20:07:29

lol @ your variable names

justinlee20:07:36

okay that now looks like my code

jsa-aerial20:07:51

I can't thank you enough - I doubt I would have figured out what was really going on - I'm guessing you looked at the code, but I probably would have given up and just used a hack

jsa-aerial20:07:17

I thought you called with a map

justinlee20:07:03

yea that’s true but otherwise the structure is exactly the same

jsa-aerial20:07:19

I mean some 'nested map' - spec is a map, but it doesn't seem to pass muster as the magic map

justinlee20:07:26

the props/children/argv thing is just gross and there’s no way around that

jsa-aerial20:07:58

It's actually just plain crazy/broken

justinlee20:07:12

i’m updating the docs and i think i’ll suggest the users just use (rest (r/argv this)) b/c that will always work

jsa-aerial20:07:13

Well, despite spent hours wandering around in the 'funhouse', I managed to fall out of the house of mirrors exhibit and am now a happy camper with code that should be ok moving forward!

jsa-aerial17:07:35

That certainly would have saved a lot of grief. But a couple of things:

jsa-aerial17:07:21

First, in "Alternately, you can use (reagent/props this) and (reagent/props children)," shouldn't that latter be (reagent/children this)? For me, that is what returns the new argv.

jsa-aerial18:07:34

Second, I still do not understand the "the arguments to your render function are actually passed as children (not props) to the underlying React component, *unless the first argument is a map.*" part. The reason is, in my case I am passing a map as (the only) argument, and the new-argv is (first (r/children this)), where (r/children this) is a vector of only the original map passed; [arg1], where arg1 is the map passed. To get the behavior of "unless first argument is a map" I had to wrap my map in another map and pass that, eg, {:val arg1}

jsa-aerial18:07:12

Here's a couple other pieces of data on this. Both of these printouts come from the same call. The first is (rgt/children this) and the second is (rgt/argv this):

jsa-aerial18:07:34

[#js {:height 200, :width 250, :data #js {:url "data/seattle-weather.csv"}, :layer #js [#js {:mark "bar", :encoding #js {:x #js {:timeUnit "month", :field "date", :type "ordinal"}, :y #js {:aggregate "mean", :field "precipitation", :type "quantitative"}}} #js {:mark "rule", :encoding #js {:y #js {:aggregate "mean", :field "precipitation", :type "quantitative"}, :color #js {:value "firebrick"}, :size #js {:value 3}}}]}]

jsa-aerial18:07:54

[#object[Function] #js {:height 200, :width 250, :data #js {:url "data/seattle-weather.csv"}, :layer #js [#js {:mark "bar", :encoding #js {:x #js {:timeUnit "month", :field "date", :type "ordinal"}, :y #js {:aggregate "mean", :field "precipitation", :type "quantitative"}}} #js {:mark "rule", :encoding #js {:y #js {:aggregate "mean", :field "precipitation", :type "quantitative"}, :color #js {:value "firebrick"}, :size #js {:value 3}}}]}]

jsa-aerial18:07:05

So, they both 'contain' the original map passed in, but the first is [the-map], but the second is [some-func, the-map]

jsa-aerial18:07:51

And (printchan :EQ? (= (first (rgt/children this)) (second (rgt/argv this))) ==> :EQ? true

jsa-aerial05:07:41

Thanks for the insight. The canvas definitely will resize at various points, so keeping it in the component-did-mount and component-did-update may be the best after all. If it is created anew each time, maybe 'did-mount' would be sufficient.

Hukka11:07:29

sigh Again I managed to get an input component that won't change its value

valtteri12:07:57

How do your :on-change and :value props for the input field look?

Hukka12:07:32

I don't even have :value for the other one...

valtteri12:07:45

Are you familiar with controlled vs. uncontrolled inputs?

pesterhazy16:07:07

Suppose I have a function like this:

(defn debounced-input
  [initial-props]
  (let [debounced-on-change
        (goog.functions/debounce (:on-change initial-props)
                                 500)]
    (fn [props]
      [:input (assoc props :on-change debounced-on-change)])))

pesterhazy16:07:37

The purpose is to debounce on-change events

pesterhazy16:07:51

This is a Form-2 component, with a debounced-on-change defined in the outer fn so that changes to the :value prop etc don't result in the creation of a new debounce-on-change

pesterhazy16:07:40

Now when the on-change prop updates for whatever reason, the component will re-render (the inner fn wil re-run) but the outer fn won't be re-run, because that's the whole point of a Form-2 comp

pesterhazy16:07:01

Unfortunately that means that changes on on-change will be dropped on the floow

pesterhazy16:07:19

You may say that on-change does not change typically, but in fact it can happen, especially when it's itself a closure which some value captured in it.

pesterhazy16:07:31

So has anyone found a pattern to deal with this sort of situation?

justinlee17:07:59

@pesterhazy I if you wrap the debouncer in another component layer, you can rely on reagent to check the props for equality, only re-debouncing (what a word) when the on-change changes (say that five times fast)

justinlee17:07:07

hm no strike that. that may not work.

justinlee17:07:28

actually you might use the trick that came up yesterday: wrap the debouncer component and force it to remount using a key metadata:

(def on-change (reagent/atom (fn [] "first on-change handler")))

(defn debouncer-component-internal
  [oc-orig]
  (js/console.log "debouncer-component-internal")
  (let [oc oc-orig] ;; debounce here
    (fn [_oc-orig]
      (js/console.log "debouncer-component-internal render")
      [:div {:on-change oc} "foo"])))

(defn debouncer-component
  [oc-orig]
  (js/console.log "debouncer-component")
  ^{:key oc-orig} [debouncer-component-internal oc-orig])

(defn test-component
  []
  (js/console.log "test-component")
  [debouncer-component @on-change])

(js/setTimeout (fn []
                 (js/console.log "updating on-change")
                 (reset! on-change (fn [] "second on-change handler")))
               1)
This prints:
test-component
debouncer-component
debouncer-component-internal
debouncer-component-internal render
updating on-change
test-component
debouncer-component
debouncer-component-internal
debouncer-component-internal render

pesterhazy17:07:00

yup that might work

pesterhazy17:07:15

here's a solution that I came up with

(defn debouncify
  "Given a Reagent component cmp that expects an on-change callback prop idenfied
  by handler-kw (often :on-change), return a component that debounces calls
  to the callback until input settles down for delay-ms."
  [cmp delay-ms handler-kw]
  (fn [initial-props]
    (let [!handler (atom (handler-kw initial-props))
          handler-debounced (gfun/debounce (fn [& args] (apply @!handler args)) delay-ms)]
      (r/create-class
       {:display-name "Demo"
        :component-will-receive-props
        (fn [this [_ new-props]]
          (reset! !handler (handler-kw new-props)))
        :reagent-render
        (fn [props & children]
          (into [cmp (assoc props handler-kw handler-debounced)] children))}))))

(defn text-input-ui [props]
  [:input props])

(def text-input-debounced-ui (debouncify text-input-ui 300 :on-change))

pesterhazy17:07:58

@lee.justin.m forcing a comp to unmount and remount feels a bit against the React spirit

justinlee17:07:17

i suppose it is matter of whether you dislike a forced remount or a form-3 component more

pesterhazy17:07:40

honestly I think form-3 components are almost inevitable

justinlee17:07:07

the moment you have to start to compare previous and next state yea

justinlee17:07:52

honestly i’m not sure which is better, but there is something expressive about the key trick

pesterhazy17:07:09

whenever you rely on a prop in the outer fn, you run the risk of using an out-of-date value when the comp rerenders with a different value for that prop

pesterhazy17:07:18

I created a Gist with a refactored version of the above: https://gist.github.com/pesterhazy/1afd10dda0449121616b54868e3d9452

pesterhazy17:07:11

I guess another question is what should happen when the on-change handler updates but there's an ongoing debounce

pesterhazy17:07:42

(a) cancel the previous debounce or (b) just continue the debounce but with the new handler?

justinlee17:07:51

what are you debouncing, out of curiosity. there are lots of input things that don’t handle well to async (as you know)

pesterhazy17:07:04

right, right!

pesterhazy17:07:09

I'm debouncing an input field

pesterhazy17:07:01

using the new adapt-input-component function dreamed up by @juhoteperi and myself: https://github.com/reagent-project/reagent/pull/381/files

pesterhazy17:07:07

this (if I do say so myself) elegantly works around the async issue by using this.setState, which applies synchronously

justinlee17:07:20

but if you debounce on-change events in an input field, won’t the later events drop the key presses in the interim?

pesterhazy17:07:24

the way adapt-input-component works is that the on-change handler is replaced with a new one that does two things - setState (which updates the <input> contents immediately) - call the original on-change handler, which is debounced

justinlee17:07:08

how does that work if the purpose of the on-change handler is to modify the input before setting a value?

pesterhazy17:07:19

ah right, it doesn't work for that purpose

justinlee17:07:21

i.e. capitalization

pesterhazy17:07:38

that's why it's not a perfect drop-in replacement for the current impl

justinlee17:07:38

i mean, that arguably is the main reason why you’d want a controlled input

pesterhazy17:07:07

really?? I use controlled input to be able to change the input contents after it has mounted

pesterhazy17:07:36

(instead of having to set the .value to "" manually when you want to reset a form)

justinlee17:07:46

why not just set the default value?

pesterhazy17:07:49

capitalization seems like a fringe use case

pesterhazy18:07:30

well if the user changed the <input> contents after it was mounted, changing defaultValue won't have any effect

justinlee18:07:43

i mean to reset a form, you don’t need to set the value in response to an on-change handler

pesterhazy18:07:45

(unless you force-remount using your key trick 🙂 )

pesterhazy18:07:01

how would you reset a search input after submitting?

justinlee18:07:44

well, using a ref, but yea that’s not very clean

pesterhazy18:07:21

I mean adapt-input-component can be changed to accept another arg, a synchronous transform-fn (by default identity) which allows capitalization etc.

pesterhazy18:07:48

but I take your point that you can get around some complexity by using defaultValue (if your use case allows for it)

justinlee18:07:45

i think allowing another arg would probably solve the problem in a complete way. a more practical use case is, for example, when people auto-format phone numbers and credit card numbers

pesterhazy18:07:38

I guess using defaultValue and setting the .value of the input manually in a wrapper when a new prop comes in would be another way to implement debouncify...

justinlee18:07:30

is it possible to make a better debounce? like, you call “changeable-debouncer”, that returns a different kind of debouncer function: one that will debounce if called using the same function but resets if you pass something new

justinlee18:07:45

it’d be like a memoized debounce

pesterhazy18:07:34

what would the function signature look like? I don't quite understand the idea

justinlee18:07:00

@pesterhazy roughly something like this (untested):

(defn changeable-debouncer []
  (let [!f   [atom nil]
        !dbf [atom nil]]
    (fn [f &args]
      (if (identical? f @!f)
        (apply @!dbf args))
      (do
        (reset! !f f)
        (reset! !dbf (debounce f))))))

justinlee18:07:08

then you’d (let [db (changeable-debouncer)] (db target-fun args))

justinlee18:07:03

repeated calls with the same target-fun would be debounced

justinlee18:07:11

still have to deal with the transition

pesterhazy18:07:50

that's a pretty cool idea

Hukka18:07:22

@valtteri I am, yes, and being uncontrolled I don't see any reason why the input won't, well, take any input. Yet that's how it is >_<

pesterhazy18:07:41

@lee.justin.m why do I still have to deal with the transition though?

justinlee18:07:00

i mean you still have to figure out policywise what you want to happen when you change target functions

pesterhazy18:07:36

yeah you may want to dispose of the first debouncer for example

pesterhazy18:07:16

the concept of "wait for things to settle down then go" is deceptively simple

pesterhazy18:07:33

lots of variations

justinlee18:07:04

yes definitely. I was thinking of the lodash implementation, for example, which is way more sophisticated

valtteri18:07:54

@tomi.hukkalainen_slac show the code, we might be able to help 🙂

Hukka19:07:40

As soon as I get this jetty accept http/2 connections 😩

pesterhazy19:07:29

@pesterhazy I’ll check that one out, thanks

justinlee19:07:03

@pesterhazy did you just @ yourself? 🙂

justinlee19:07:14

btw, if you have a moment, does this make any sense to you: https://gist.github.com/jmlsf/7fe736c8b6d7f236361aaad59a1113fd

😜 4
justinlee19:07:37

why would component-did-mount receive the old argv and how do you get the new one

justinlee19:07:00

it works if you pass a props map but not with argv convention

pesterhazy19:07:28

@lee.justin.m did you mean did-update?

justinlee19:07:16

oh hang on that’s what it’s supposed to do.

justinlee19:07:31

i guess you have to use r/props and r/children. a bit weird there’s no clean way to get the argv

pesterhazy19:07:50

For an attempt at a better api

pesterhazy19:07:15

Reagent is a bit “funny”

justinlee19:07:12

i continue to think the argv mapping wasn’t worth the trouble

justinlee19:07:24

i’ve been bitten by this so many times

justinlee19:07:48

recalcitrant looks interesting

pesterhazy19:07:57

Yeah I agree, a single prop map is better almost always

pesterhazy19:07:33

Maybe time for a simplified reagent v2?!

justinlee19:07:22

well okay so there’s a reagent/argv too

justinlee19:07:25

@pesterhazy funnily enough, props maps was the original way reagent worked looking back on the change history

😱 4