Fork me on GitHub
#re-frame
<
2020-05-20
>
Jakob Durstberger06:05:50

Morning, sorry for the newbie question. I am building a sign in component and my input is laggy. I looked at the docs and I think I have found a solution. Before -> Full event loop round trip

[:input.input
          {:type "text"
           :value @(rf/subscribe [::subs/username])
           :on-change #(rf/dispatch [::events/change-username (-> % .-target .-value)])
           }]
After -> Using local reagent atom
(let [username (reagent/atom "")]
    (fn []
         [:input.input
          {:type "text"
           :on-change #(reset! username (-> % .-target .-value))
           }]
Is this idiomatic and normal to have state outside the db? Is it normal to see laggy input for a regular event round trip or am I doing something wrong in the Before bit? Thanks :)

p-himik06:05:03

Impossible to say anything without access to the full code. Your ::events/change-username event handler may be non-trivial, it can have some costly interceptors, or you may have some subscription handlers that do a lot of processing. Or you may have a lot of non-trivial level-1 subs handlers. Or maybe something else. Can you reproduce the behavior in a test app where there's nothing but that input field, that one sub, and that one event implemented trivially?

Jakob Durstberger07:05:59

Yeah I should have clarified, that the handlers just update the db.

(defn handle-change-username [db [_ username]]
   (assoc db ::state/username username))

(rf/reg-event-db ::change-username handle-change-username)
I’ll see if I can reproduce this in a test app.

p-himik07:05:20

Consider also giving https://github.com/day8/re-frame-10x a try. It may tell you where the time is spent.

mikethompson08:05:14

@jakob.durstberger Do you have the option of using onblur instead?

mikethompson08:05:08

If you have to use onchange then consider using dispatch-sync which is designed for this kind of tight loop.

p-himik08:05:18

@U051MTYAB Even with regular dispatch, there should be no perceivable delay, no?

mikethompson08:05:46

dispatch happens "very soon" (up the the browser what that means)

Jakob Durstberger08:05:03

I don’t think I have to use on-change it’s just the call back I found in the docs first 😄 I can try onblur and dispatch-sync but will only be able to do so after work.

mikethompson08:05:55

on-blur will make the problem disappear.

mikethompson08:05:01

If that's an option

mikethompson08:05:32

Because then there is no tight loop, with the user typing and events getting dispatched and then the component getting an animation-frame 16ms later.

Jakob Durstberger08:05:36

I’d have to try. Ideally I’d want a user to be able to hit the enter key when they are still on the password field to try to sign in.

mikethompson08:05:46

Remember also that some component libraries, like re-com already go to a bit of effort to handle this sort of issue

mikethompson08:05:24

Of course, the component is doing A LOT, so its a bit complicated https://github.com/day8/re-com/blob/master/src/re_com/misc.cljs#L72-L181

Jakob Durstberger08:05:05

I found re-com a while ago and I want to create my app to be a PWA and therefore mobile first. I didn’t look further into it as it states that it doesn’t support mobile

mikethompson08:05:09

So, summary, four possible solutions: 1. use on-blur 2. use dispatch-sync 3. user re-com (or other) 4. roll your own component which copies the sort of technique used by re-com (but is much simpler). The technique is the have local state

mikethompson08:05:58

Any one of those solutions should work. No need to combine them.

mikethompson08:05:36

ALSO there's nothing wrong at all with the suggestions by @U21QNFC5C ... they are good ideas .... except I'm not sure they'll get you far enough if the user types very quickly. Not sure.

Jakob Durstberger08:05:07

Thank you all, I’ll try those today after work or tomorrow morning 🙂

Lu16:05:05

If you want to go for the laziest approach you can try out: https://github.com/luciodale/fork

Jakob Durstberger06:05:08

I just tried out dispatch-sync and it works like a charm. I’ll definitely keep on-blur in mind

ingesol06:05:00

@jakob.durstberger You should not see a performance lag in your “full roundtrip” version. Only if you have hundreds or thousands of these would we start looking at performance. I think it’s more likely you’re hit by issues with react and controlled text inputs. You should be able to google material about that. About “idiomatic”, it depends how important it is to you that your UI is 100% in sync with your DB. Things like time travel stop working with your approach above, but if you don’t want/need that you’re perfectly fine.

ingesol06:05:09

But to test the perf thing, try putting your subscription and on-change handler in a let, like this, that might be more efficient:

(reagent.core/with-let [sub (f/subscribe [...]
                        on-change #(f/dispatch...))]
  [:input.input
          {:type "text"
           :value @sub
           :on-change on-change)
           }) 

Jakob Durstberger07:05:13

Thanks I’ll give that a go

sandbags10:05:34

I'm coming back to an old re-frame app, lein ancient upgraded a lot of dependencies including reagent which is now triggering warnings (e.g. reagent.core/render is deprecated). Since my app is based on the reframe-template can I just swap out the core.cljs file with the latest version of the template? (I would create a new app with the same name and options and then copy the expanded core.cljs)

sandbags10:05:37

Or maybe I would be better off generating a new app and copying my own code over to it?

sandbags10:05:36

Ah, that's useful — thanks Mike

mikethompson11:05:48

@sandbags Depending on how many changes you made after generating from the template, it might still be worthwhile regenerating from the template and copying in your code.

mikethompson11:05:16

We have been doing a bit of work on the re-frame-template to make it work better with shadow-cljs

kishanov19:05:16

Hi there. I’m trying to use re-frame-10x with existing re-frame app, but getting an exception at the moment of rendering root component:

TypeError: Cannot read property 'call' of undefined
    at re_frame_10x.cljs?rel=1590003075265:29
which points to this chunk of code:
(trace/with-trace {:op-type   :render
                          :tags      (if-let [component-name (component/component-name c)]
                                       {:component-name component-name}
                                       {})

kishanov19:05:21

Any clue what might be happening?

kishanov20:05:29

found the issue, it was outdated re-frame/reagent dependencies. Sorry for the noise

nprbst23:05:09

Noob Q: Is there an “approved” way of partitioning the app-db to separate ephemeral state (navigation state) from durable state (domain data, possibly shared) such that ephemeral is in-memory only, but durable can be backed by something like re-frame-firebase (https://github.com/deg/re-frame-firebase)? Or am I thinking about this wrong?

lilactown23:05:43

reading re-frame-database, it seems like you would read and write directly to the firebase db using effects/subscriptions in the firebase namespace

lilactown23:05:11

so your durable data would live in firebase, and your ephemeral state would live in the app-db