re-frame/react - controlled input losing key presses

179 views
Skip to first unread message

Colin Yates

unread,
Apr 3, 2015, 5:25:24 PM4/3/15
to clojur...@googlegroups.com
Hi,

If I use :default-value then the input field keeps up no matter how fast I type. If I use :value then I can lose quite a few key presses if I type really quickly.

I expect I probably need to break my components into smaller discrete components, which only depend on the absolute minimum, but I wondered first whether anyone else has experienced this and has a workaround?

At the moment my components are quite large, for example I have one component which has 10 fields on it - splitting that into 10 separate components with more focused subscriptions will help I assume?

This is on a late 2008 macbook pro so isn't the quickest, but still...

Thanks!

AndyR

unread,
Apr 3, 2015, 5:56:06 PM4/3/15
to clojur...@googlegroups.com
IMO, it should be avoided to have a controlled input. As you already mentioned the problems. But even if I need to keep it --e.g.-- to numbers: A much more user friendly UI is to present the user with an error message (next to the input) stating exactly what's wrong with his/her input.
The other typical example like "all capital" can easily be applied afterwards when sending the content to the server.

I haven't tried this but it probably works: You can also just listen to "onkeypress" and just prevent the default (ie the browser populates .-value) and populate it yourself (filtered). This means that instead of losing input you'll just have a lagging UI. Better, but not optimal. This brakes down for copy and paste (and possibly other input?), so you'll still need to listen to on-change.

Sometimes, I even lose keypresses with my fast PC since my computer is busy with other stuff. So the controlled input which relies on timing is a big no-no IMO.

Colin Yates

unread,
Apr 3, 2015, 5:59:27 PM4/3/15
to clojur...@googlegroups.com
The reason I want it controlled is because I need to change the state the input is bound to via another mechanism. If an entity is recognised then I show a "do you want to load the existing user" for example. If they chose to then the atom is updated but not the Input field (with default-value).

Sent from my iPhone
> --
> Note that posts from new members are moderated - please be patient with your first post.
> ---
> You received this message because you are subscribed to the Google Groups "ClojureScript" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to clojurescrip...@googlegroups.com.
> To post to this group, send email to clojur...@googlegroups.com.
> Visit this group at http://groups.google.com/group/clojurescript.

Andre Rauh

unread,
Apr 3, 2015, 6:35:35 PM4/3/15
to clojur...@googlegroups.com
Other than resorting to more manual steps: One easy "hack" is to just change the ^{:key "..."} meta attribute of your [:input ]. This will unmount and re-mount it and re-populate the :default-value.

You received this message because you are subscribed to a topic in the Google Groups "ClojureScript" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojurescript/mqxEiTw6XU4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojurescrip...@googlegroups.com.

Colin Yates

unread,
Apr 4, 2015, 4:21:06 AM4/4/15
to clojur...@googlegroups.com
That might work - nice, I will try. I was pondering whether binding to a local atom and periodically synchronising with global state might work, but your approach is much nicer.

Marc Fawzi

unread,
Apr 4, 2015, 9:09:49 AM4/4/15
to clojur...@googlegroups.com
<<
 If I use :value then I can lose quite a few key presses if I type really quickly.
>>

Hi Colin,

I'm very interested i seeing the code for that. The reason is I have been working on behavior encapsulating "smart" input component with animations and I've yet to optimize performance but at the same time I don't have your issue with fast typing. Maybe the performance issue is something to do with reframe's register/subscribe and higher levels of abstraction vs simply using cursors?

Marc

Colin Yates

unread,
Apr 4, 2015, 9:19:06 AM4/4/15
to clojur...@googlegroups.com
Hi Marc, sure - but give me a few days. It isn't anything more complex
than (hacked from memory, and obvious numpty upgrades
notwithstanding...):

(register-sub :add-patient/filter
(fn [db] (reaction (-> db deref state/AddPatient :filter)))

(register-handler :add-patient/edit-field
[c/middleware (path state/AddPatient :filter]
(fn [db [k v]] (assoc db k v)))

(defn []
(let [state (subscribe [:add-patient/filter])
field-a (reaction (-> state deref :field-a))]
[:input {:type "text" :value @field-a :on-change (fn [] (dispatch
[:add-patient/edit-field :field-a %]) nil)}]))

whodidthis

unread,
Apr 4, 2015, 2:38:11 PM4/4/15
to clojur...@googlegroups.com
Yes you will lose everything if you change input value through dispatch as it is asynchronous. Something synchronous like a local atom will work though. As does re-frames app-db.

Basically you need something like 'dispatch-sync' (https://github.com/Day8/re-frame/blob/master/src/re_frame/router.cljs#L54) except dispatching resets entire app-db so that wont work for reasons mentioned in the other thread. You need to swap app-db on your own.

(register-sub
::my-secret
(fn [db _]
(reaction (::my-secret @db))))

(defn synced-change
[value]
(swap! re-frame.db/app-db assoc ::my-secret value))

(defn tags-show
[]
(let [value (subscribe [::my-secret])]
(fn []
[:input {:value @value :on-change (fn [e] (synced-change (-> e .-target .-value)))}])))

Maybe in the future re-frame will have a scoped dispatch-sync or whatever.

Colin Yates

unread,
Apr 4, 2015, 3:08:32 PM4/4/15
to clojur...@googlegroups.com
Thanks whodidthis.

Marc Fawzi

unread,
Apr 4, 2015, 5:33:01 PM4/4/15
to clojur...@googlegroups.com
From leaking to leaky? There must be another way. But can you explain @whodidthis (awesome nick) why async dispatch loses data when you type fast? Very curious!

Sent from my iPhone

whodidthis

unread,
Apr 4, 2015, 5:59:13 PM4/4/15
to clojur...@googlegroups.com
Probably something like when you type quickly letters a b c d and they are handled by re-frame sequentially with view updated every requestAnimationFrame (16ms?) so the following happens:

type a dispatch a
view a
type b dispatch ab
type c dispatch abc
view ab
type d dispatch abd
view abc
view abd

So input now has value abd even when you typed abcd. But it's not just loss of data, it also messes up ctrl+z undo and other stuff https://github.com/Day8/re-frame/issues/39

Mike Thompson

unread,
Apr 4, 2015, 9:54:25 PM4/4/15
to clojur...@googlegroups.com
I've added these notes to issue 39. I tend to use onblur as our update trigger (not char by char) and so I don't run into this problem, so i don't have a solution handy.

--
Mike

whodidthis

unread,
Apr 5, 2015, 3:20:51 PM4/5/15
to clojur...@googlegroups.com
On Saturday, April 4, 2015 at 9:38:11 PM UTC+3, whodidthis wrote:
> Yes you will lose everything if you change input value through dispatch as it is asynchronous. Something synchronous like a local atom will work though. As does re-frames app-db.
>
> Basically you need something like 'dispatch-sync' (https://github.com/Day8/re-frame/blob/master/src/re_frame/router.cljs#L54) except dispatching resets entire app-db so that wont work for reasons mentioned in the other thread. You need to swap app-db on your own.
>
> (register-sub
> ::my-secret
> (fn [db _]
> (reaction (::my-secret @db))))
>
> (defn synced-change
> [value]
> (swap! re-frame.db/app-db assoc ::my-secret value))
>
> (defn tags-show
> []
> (let [value (subscribe [::my-secret])]
> (fn []
> [:input {:value @value :on-change (fn [e] (synced-change (-> e .-target .-value)))}])))
>
> Maybe in the future re-frame will have a scoped dispatch-sync or whatever.

Okay, ignore this advice. Dispatch-sync instead of dispatch will work just fine for controlled inputs.

I misunderstood some warnings on the other thread, sorry everyone
Reply all
Reply to author
Forward
0 new messages