Fork me on GitHub
#reagent
<
2017-08-31
>
Oliver George01:08:48

Quick sanity check. Since (not= #() #()) does that mean using inline functions as callbacks causes more re-rendering work than static functions?

noisesmith01:08:40

the inline function should only get created once anyway

noisesmith01:08:04

it's a good reason to not create functions inside eval, inside your component though

noisesmith01:08:11

(as if we needed another one)

Oliver George01:08:14

Thanks. What do you mean by "inside eval inside"

noisesmith01:08:53

inside eval, inside your component

Oliver George01:08:54

You mean inside render fn?

noisesmith01:08:53

it would have to be inside the data your component reads in order to render, now that I think about it

Oliver George01:08:10

Yeah, we're having trouble thinking our way out. You need the render context for most interesting inline callbacks

Oliver George01:08:21

(without resorting to self/this )

noisesmith01:08:45

right, anyway, in order for a function non-equality to make an extra render happen, you would have to do something convoluted and weird

noisesmith01:08:06

unless of course the function was a replacible part of your render state, in which case I guess... you must have wanted that

Oliver George01:08:06

We found a bug in https://github.com/PaulLeCam/react-leaflet where the prop change associated with an inline function update during render caused a full redraw which was heavy / slow.

Oliver George01:08:14

That's not the typical case

Oliver George01:08:30

I think react would simply update the onClick callback value which is not heavy

Oliver George01:08:48

but still, interesting to note that it's extra work where a data value would not have changed

Oliver George01:08:00

for re-frame you really want :on-click to accept a vector and dispatch

Oliver George01:08:05

but that's a big change

Oliver George02:08:54

And for amusement as much as anything else here's an example of how you could create a :on-click handler which dispatches but uses data equality...

(defn build-dispatch [& args]
  (specify! #(dispatch args)
    IDispatch
    (-dispatch-args [_] args)
    IEquiv
    (-equiv [o other]
      (if (implements? IDispatch other))
        (= args (-dispatch-args other)))))

Oliver George02:08:12

aaand it already exists but better in reagent.core ns !!!

Oliver George02:08:17

(defn partial
  "Works just like clojure.core/partial, but the result can be compared with ="
  [f & args]
  (util/make-partial-fn f args))

jeaye03:08:46

I have a TextInput for currency and I'm formatting the value when it changes. Unfortunately, this causes a lot of flickering, since, when I input a number, it goes into the input, then the input refreshes with the new format.

jeaye03:08:40

Is there a way around this issue? It's truly annoying, as a user.

jeaye03:08:54

; Using re-frame, the code looks something like this
[text-input {:keyboard-type :numeric
                      :placeholder (fmt/money 0)
                      :value (fmt/money amount)
                      :on-change-text #(dispatch-sync [::events/set-bet (fmt/parse-money %)])}]

noisesmith03:08:12

that reminds me, is there a standard debounced input component or helper? (that's what I'd try to find or make for something like that but I have found other work arounds in the past)

jeaye03:08:04

I don't mind using create-class and cooking something up, but I haven't found a single result when searching how to make a custom text input. i.e. something that spawns the keyboard, listens to key press events, and maintains the input state.

jeaye03:08:21

I'd basically want to make something just like TextInput, except it doesn't append each char to the text value. Instead, it just calls a fn with each text change.

noisesmith03:08:05

what if the edits were controlled by the user and not changed by the widget until they exit focus, with the resulting value previewed in a clickable dropdown?

noisesmith03:08:11

kind of like the UI of autocorrect

noisesmith03:08:27

that seems doable with a standard text widget plus some dropdown boilerplate

jeaye03:08:17

I've thought about something like that. Since the value would be updated on blur, for example, there would still be the issue of re-editing the now-formatted value. So I'd have to unformat it upon editing and format it after editing.

jeaye03:08:04

All of that seemed like as much work as should be required to do it a "better" way, but that may not be the case.

pesterhazy13:08:44

@jeaye that's on React Native, right?

pesterhazy13:08:11

I think the issue may be a reagent problem - DOM changes are made asynchornously, in the next tick

pesterhazy13:08:23

One thing you could try is (1) use a local state atom as the immediate backing buffer of the text-input, and (2) if that works sync it with re-frame.

pesterhazy13:08:13

Also you need to call (r/force-update this) to short circuit reagent's event batching mechanism

jeaye17:08:28

@pesterhazy Yep, it's on React Native.

jeaye17:08:02

@pesterhazy Is there an example for that anywhere? Even accessing this requires r/current-component, right?

jeaye18:08:43

I ask because r/current-component is always nil in these callbacks, so I'm not quite sure how I'm meant to access this.

pesterhazy18:08:41

here's how I do it

jeaye18:08:00

😄 Getting me all excited that this can be fixed.

pesterhazy18:08:28

this is extracted and not tested in its current form, but it should help make the idea more concrete

pesterhazy18:08:56

why bind on-change in a let? Because that way, the prop doesn't change on each render, causing unnecessary rerenders.

jeaye18:08:03

Awwww yeah.

jeaye18:08:37

I'm gonna give this a go and make any necessary tweaks. Thank you so much for illustrating this.

pesterhazy18:08:14

updated - I forgot to use !value

pesterhazy18:08:43

I wish slack didn't auto-expand gists

pesterhazy18:08:31

please check the Gist, not the preview

pesterhazy18:08:39

oh, and one problem is that this won't take into account changes of the value prop after it has been mounted - that may or may not be a problem depending on the use case

pesterhazy18:08:13

you could expand this to sync back prop changes but that makes things a bit more complicated

jeaye19:08:58

I still see a delay from when I hit a number and it shows up in the text box to when it's formatted properly.

pesterhazy19:08:08

so it first shows up unformatted, then formatted?

jeaye19:08:33

I've updated the gist with how I'm using it.

pesterhazy19:08:49

what if you don't update the value prop at all - does it stay frozen or can you still type?

jeaye19:08:12

Typically the typing still works fine. I'll check with the instant-text-input.

jeaye19:08:35

Changing to :default-value

pesterhazy19:08:22

this mentions flickering

jeaye19:08:33

Yep, I've read that. Doesn't suggest any ways around it though.

jeaye19:08:53

So, with :default-value instead of :value, I still see it unformatted first.

jeaye19:08:50

I can make a recording, if you'd like.

pesterhazy19:08:11

that'd be great

pesterhazy19:08:35

just tried this

(defn test-ui []
  [n/text-input {:style {:margin-top 50
                         :margin-left 5
                         :background-color "#facade"}
                 :placeholder "bla"
                 :value "foo"}])

pesterhazy19:08:43

I see flickering even in this simple case

pesterhazy19:08:22

if you type, you see the char for a split second, then the change gets reverted

pesterhazy19:08:52

so this seems like a limitation of RN?

jeaye19:08:22

Recorded session, since I've already made it anyway. 😛 https://upload.jeaye.com/tmp/out.ogv

pesterhazy19:08:04

yeah that's janky as hell

pesterhazy19:08:49

I'd guess updating the value as you type is not a supported use case of TextInput

pesterhazy19:08:11

You could try raising an issue on github...

jeaye19:08:27

I wonder if setNativeProps can be used on this in the new on-change-text.

jeaye19:08:17

Yeah, exactly what I was going on.

jeaye19:08:51

(.setNativeProps @this' (clj->js {:text (format-value new-value)})) Seems like the fn doesn't exist though; it's undefined.

pesterhazy19:08:11

you need to call it on the actualy text input

pesterhazy19:08:42

[text-input {:ref #(reset! !input %))]

pesterhazy19:08:53

with (let [!input (atom nil)] ...)

pesterhazy19:08:27

then (some-> @!input (.setNativeProps #js {:text "asdf"}))

jeaye19:08:57

Yep, you're quite right.

jeaye19:08:04

Same lag, unfortunately.

pesterhazy19:08:22

yeah it's probably not possible currently

pesterhazy19:08:32

unless you write a native module

jeaye19:08:31

Well, it's good to know. Thanks, again, very much for working with me on this one.

pesterhazy19:08:48

I've run into issues with TextInputs many times myself

jeaye19:08:53

I'm gonna compare the instant-text-input to the normal text-input, side by side, to see if there's a noticeable difference.

jeaye19:08:01

If so, I may stick with it.

yedi20:08:26

whats the function for converting hiccup into jsx so that it can be used by a plain react component

yedi20:08:13

the react component takes jsx as a prop, but when i pass it hiccup it fails

yedi20:08:53

ah i think it's as-element

pesterhazy20:08:15

it's not JSX

pesterhazy20:08:33

JSX is just syntactic sugare for createElement

pesterhazy20:08:45

so as-element is right

pesterhazy20:08:05

another option is reactify-component

pesterhazy20:08:03

normally components don't take elements as props but functions that return elements, i.e. components

pesterhazy20:08:21

maybe you are working with a special case?

yedi20:08:04

i created the plain react component that im talking about

yedi20:08:16

so i guess what i did is non standard