Fork me on GitHub
#reagent
<
2021-03-02
>
pez11:03:12

Hello folks. I am running into problems with reacting to the current time there is some background here https://clojurians.slack.com/archives/C073DKH9P/p1614352956019800, but will try to frame it better here. I have components that should change how they look, what they show, and what they dispatch on press (it’s react native, but I doubt that matters). The app only cares about minutes, and I want the components to react on the minute, w/o having very high requirements on performance. For this I created a ratom like so:

(defonce current-time (r/atom (t/now)))
(js/setInterval (fn []
                  (reset! current-time (t/now)))
                (* 1000 1))
And then in the components I deref it via a function that looks a bit like so:
(defn active-at? [some-time event]
  (and (t/after? @time/current-time some-time)
       (not (some #{event} [:event/foo
                            :event/bar
                            ,,,]))))
The app behaves perfectly from the looks of it! But everything is not dandy. After some minutes I get warnings like this
Warning: Please report: Excessive number of pending callbacks: 501. Some pending callbacks that might have leaked by never being called from native code: {"471":{"module":"UIManager","method":"configureNextLayoutAnimation"},"665":{"module":"UIManager","method":"configureNextLayoutAnimation"},"1192":{"module":"UIManager","method":"configureNextLayoutAnimation"},"1553":{"module":"UIManager","method":"configureNextLayoutAnimation"},"1896":{"module":"UIManager","method":"configureNextLayoutAnimation"},"1897":{"module":"UIManager","method":"configureNextLayoutAnimation"},"1923":{},"1925":{"module":"UIManager","method":"configureNextLayoutAnimation"},"1934":{"module":"UIManager","method":"configureNextLayoutAnimation"},"1936":...
I’ve tried with making the components be of form-2, but that doesn’t help. (Why it would help, I don’t know, this is a bit beyond what I currently grasp about reagent.) Anyone been down this path, or just anyway know how I should navigate? ❤️

p-himik11:03:02

First of all, don't call js/setInterval at the top level of your NS. Make it a part of defonce, otherwise it will be called on each code reload (apologies if it works differently on React Native - I've never worked with it so I have no idea). Second of all, don't track seconds if you care only about minutes. Leave the interval to be one second, but store only minutes in current-time and call reset! when the next minute comes. I'm not sure whether that will fix the "Excessive number of pending callbacks" warning, but it's something that I definitely would've done.

pez11:03:58

Thanks. Will do all this. (I don’t think it works differently in react native). How ever, afaiu this would make the problem 60x smaller, but it would still be there…

pez11:03:14

btw, I first had a setTimeout wrapping the setInterval where I tried to start the interVal on a minute sharp, to fire every minute, but I had some bug in that. And also thought I might get drifts from it. Your suggestion sounds like a better idea.

p-himik11:03:39

Precursory searching shows that people have this problem because of promises. And your code with the atom doesn't use any. Are you sure this problem wasn't there before? Maybe it was simply exacerbated by the changes made every second?

p-himik11:03:48

A relevant issue on GitHub with some discussion: https://github.com/facebook/react-native/issues/27483

pez11:03:40

I haven’t even googled the problem! facepalm . Many thanks, will check out. The problem goes away if I do not deref that atom, btw.

p-himik11:03:51

It doesn't mean that it's the atom to blame though. Derefing it in a view causes redraws whenever that atom changes. To confirm that it's not the atom, you can try queuing a redraw every second.

mikethompson11:03:02

I am guessing wildly ... but could it be that your process is being throttled in some way?? Which means there are just too many setIntervals callbacks queued up. If this guess is correct, maybe this rewrite (untested) using setTimeout will take the pressure off:

(defn reset-time
  []
  (js/setTimeout #((reset! current-time (t/now))
                   (reset-time))
    (* 1000 1)) 

(reset-time)
Ie. don't queue up another callback (via setTimeout) until the last one fired. Just a wild guess. @pez

pez12:03:46

Thanks, @mikethompson! ❤️ Unfortunately that didn’t do it. Maybe I’ll just hide the problem by employing @p-himik’s suggestion to use store whole minutes in the ratom. Trying it this way now:

(reg-sub
  :current-time
  (fn []
    my-time/current-time)
  (fn [t]
    t))

(reg-sub
  :current-minute-time
  :<- [:current-time]
  (fn [t _]
    (t/floor t t/minute)))
So far, no warnings. 😃 The last warning took some 4 minutes to appear so now it should take 4 hours, I think. From the SO-thread, maybe it also is only happening while the debugger is attached. Not at all sure what to do with it… There is only so much time allotted to me in this world!

pez17:03:08

OK. So this “works” in hiding the issue. But now I have created a new issue, perhaps @p-himik called it above. When shadow-cljs reloads the code, the screen with these time components is shown (or navigated to, I am not sure yet what happens). I tried to hide the setInterval inside the defonce like so:

(defonce current-time
  (do
    (js/setInterval #(reset! current-time (t/now))
                    1000)
    (r/atom (t/now))))
The screen in questions has lots of other subscriptions and things have been dandy before…

p-himik17:03:44

From your description, I'm not sure what the issue is.

pez17:03:35

Let me try again then. The issue is that when I save a file, the app shows the screen with components that is subscribing to the time. So the screen I am working with goes away. So it is much more like Browsersync or other JS tech behaves.

p-himik17:03:37

Ah. To confirm: You're working on screen A. Only the screen B uses current-time. When you make any change in any file, the UI switches to the screen B. Correct?

pez17:03:55

Exactly.

pez17:03:44

Screen B happens to be the start screen. Which might matter.

p-himik17:03:33

It does matter. The screen A is shown because of some change you make in the whole state of the UI via navigation. That state is not stored in defonce, so it disappears because of code reload.

p-himik17:03:59

Same as evaluating

(def a (atom nil))
(reset! a 7)
(def a (atom nil))

p-himik17:03:37

If you're still using re-frame, then the way the current screen is determined should rely on app-db.

pez17:03:07

So, unrelated to the current-time, thingy?

p-himik17:03:54

Unrelated.

pez17:03:43

Indeed. Switching to where branched off I have the same behaviour. Thanks!

👍 1
Aaron Decker19:03:05

Does anyone have impressions to share on drag'n'drop libraries that have worked for you in your Reagent apps? It looks like my main options are https://github.com/react-dnd/react-dnd or going cljs-native with https://github.com/Kah0ona/re-dnd but I wanted to see if the community had any recommendations.

juhoteperi19:03:26

Depends on what you need. In general I prefer using React libraries directly. They are usually well documented and have loads of users. If you just need to support dropping files in a div, you could just implement everything yourself. On-drop handler is like 20 lines, and a few lines more to track drop over state: https://github.com/metosin/komponentit/blob/master/src/cljs/komponentit/filepicker.cljs#L71-L104

👍 1
rberger02:03:29

We were able to use https://github.com/atlassian/react-beautiful-dnd in our reagent/re-frame app. But it (and DnD in general) were the hardest for me to get my mind around to get into reagent / JS interop. Have a very simple literal translation from a JS example to a CLJS / Reagent in https://github.com/omnyway-labs/simple-list-dnd We since made a more Clojurescript / reagent style implementation but its kind of embedded in our app for now

juhoteperi19:03:26

Depends on what you need. In general I prefer using React libraries directly. They are usually well documented and have loads of users. If you just need to support dropping files in a div, you could just implement everything yourself. On-drop handler is like 20 lines, and a few lines more to track drop over state: https://github.com/metosin/komponentit/blob/master/src/cljs/komponentit/filepicker.cljs#L71-L104

👍 1
Dmytro Bunin19:03:08

I am having some fun time with mui component and it’s inputs and got stuck a bit. Basically, I am trying to use https://material-ui-pickers.dev/api/KeyboardDatePicker and following [this](https://github.com/reagent-project/reagent/blob/master/doc/examples/material-ui.md) example I managed to get the input somewhat working, but it still doesn’t feel quite right. This is what I got now:

[mui/keyboard-date-picker
 {:variant   :inline
  :format    "dd/MM/yyyy"
  :InputProps {:inputComponent mui/input-component}
  :value     (values :date)
  :on-blur   handle-blur
  :on-change (fn [event _]
               (set-values {:date event}))}]
But when I input the date it goes something like 10/10/0002, when I type 10/10/2. Basically, I am not sure on the direction I can take now so I would really appreciate any suggestions.

p-himik19:03:10

I don't know for sure, but seems like :on-change is called either on every keystroke or on every input that it can possibly parse. If so, you could check in :on-change whether the string is complete and only then call set-values.

Dmytro Bunin19:03:06

it’s basically nil until it’s fully set

Dmytro Bunin19:03:22

actually I’m wrong

Dmytro Bunin19:03:29

okay thanks, I think I got it

Dmytro Bunin19:03:38

re-wrote on-change with

:on-change (fn [event text]
  (prn :text text)
  (when-not (and (string? text)
                 (re-find #"\_" text))
    (set-values {:date event})))
works like a charm partywombat

👍 1