reagent

jcb 2024-02-02T18:15:50.314729Z

I'm trying to use the event handler for onscrollend (https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollend_event). However, I'm getting "Warning: Unknown event handler property onscrollend. It will be ignored." Has anyone come up against this before? onscroll works as expected e.g [:div {:onscroll #(function)}]. The issue that I'm trying to solve is that I have very large clickable buttons inside a scrollable element. Every so often on touchscreens the on-click function is being triggered on these when scrolling. I can disable them using the onscroll and want to re-enable them using onscrollend. Seems like the simplest solution to the issue.

DrLjótsson 2024-02-03T15:03:08.217899Z

Somewhat off topic: how come react does not simply accept events by their name? Why do new events need to be defined in react to work?

jcb 2024-02-03T15:11:18.740189Z

I'm attempting the debounce method like in the first answer here https://stackoverflow.com/questions/66139456/click-event-is-triggered-by-scrolling. Trying to hold a variable high while the event is happening. However, it's not working too well, i.e it still fires off the on-click fn-a while scrolling. I think I don't understand the interop around making interval functions work within reagent's atoms - here's the basics of my attempt-

(defonce is-scrolling? (atom false))

(defn main-menu []
      (with-let [data (:data @content)
            albums (all-leaves data)
                 timeout-fn (atom nil)
            ]
          [:div {:style {:height "50px"}} (str @is-scrolling?)]
          [:div.row {:id "main menu"
          	:ref  (fn [el] (reset! side el)
                                  )
            :onScroll (fn [e]
                      (reset! is-scrolling? true)
                      (if @timeout-fn (do (js/clearTimeout timeout-fn) (reset! timeout-fn nil))
                            (reset! timeout-fn (js/setInterval #(reset! is-scrolling? false) 500))))
                    }
                       [:div
                       	{:onclick (fn []
                                	(if (false? @is-scrolling?)
                                        (fn-a)
                                        (fn-b)
                                         ))} 

                       "CONTENT"]]
                       
             (finally (js/clearInterval @timeout-fn))
       )
)

p-himik 2024-02-03T15:15:10.224059Z

> Why do new events need to be defined in react to work? @brjann I don't know for certain, but maybe because React wraps events in its own machinery (e.g. each event handled by React is wrapped in a synthetic event).

👍 1
p-himik 2024-02-03T15:24:46.659339Z

@jcb A few comments: • Use global atoms only for truly global things, like app-wide state. The internal state of a component should be defined in that component. is-scrolling? sounds like a component's internal state • Don't use :refer, use :as, so e.g. with [reagent.core :as r] you can use r/with-let. Helps immensely with seeing what's what at a glance, especially those atom calls (I have no idea whether those are from Reagent or CLJS) • js/clearTimeout receives a timeout ID, but you're feeding it an atom • js/setInterval should be used in tandem with js/clearInterval, not js/clearTimeout • But it seems that you actually need to use js/setTimeout here • No need for that finally unless you want is-scrolling? to remain true if the component is unmounted for some reason Some minor things: • You can use 50 instead of "50px" • No need for that false? - you can use if-not or regular if if you swap the branches • You can use :on-scroll and :on-click for event names to make them more CLJ-ey • The code might be a tiny bit simpler if you use https://google.github.io/closure-library/api/goog.async.Debouncer.html. But I myself would also use js/setTimeout here, probably

jcb 2024-02-03T15:27:28.490799Z

Sorry, yes some of these mistakes crept in while trying to create a basic example without all the layout context.

jcb 2024-02-03T15:52:54.007749Z

"`js/clearTimeout` receives a timeout ID, but you're feeding it an atom" I thought that would just point to what was inside the atom?

jcb 2024-02-03T16:13:48.410879Z

"Don't use :refer, use :as, so e.g. with [reagent.core :as r] you can use r/with-let. Helps immensely with seeing what's what at a glance, especially those atom calls (I have no idea whether those are from Reagent or CLJS)" They are reagent atoms at the moment

p-himik 2024-02-03T16:26:13.805039Z

> I thought that would just point to what was inside the atom? If you deref the atom, yes. But in that code above, there's no deref or @: (js/clearTimeout timeout-fn).

p-himik 2024-02-02T18:40:36.851899Z

scrollend got added to the spec very recently. React added support for it in its code just four months ago: https://github.com/facebook/react/commit/537228f9fd703d18bea1f6d20fa0e5006b795c42 To use that event with React (assuming your target browsers don't include Safari that still doesn't support it), you'd have to use an unreleased version of React.

jcb 2024-02-02T18:44:50.482169Z

Thanks, is this the case even using a longhand event listener?

p-himik 2024-02-02T18:51:11.844289Z

If you mean onScrollEnd then yes, it's the same thing.

p-himik 2024-02-02T18:52:53.482639Z

If only partial support from modern browsers doesn't bother you, you can add an event handler for the scrollend event imperatively via a ref. A caveat - you'll have to handle any changes to the handler yourself.

jcb 2024-02-02T19:00:54.769449Z

I'm not sure I follow, but thanks for your help

jcb 2024-02-02T19:03:46.736119Z

Do you mean adding it inside the ref function? when it is mounted?

jcb 2024-02-02T19:15:41.287109Z

Is it possible in reagent to track an event so that it changes an atom when it is happening and after it has fired? Or do I need to manually debounce the scroll event with a timeout?

p-himik 2024-02-02T19:45:50.426629Z

> Do you mean adding it inside the ref function? when it is mounted? Yes. But if you ever decide to change the event handler without re-mounting the component, you need to do it manually. IMO the best way is to use an event handler object instead of a function and just replace its handleEvent attribute with the new handler. > Is it possible in reagent to track an event so that it changes an atom when it is happening and after it has fired? Sorry, no idea what you mean.

jcb 2024-02-02T20:19:35.187779Z

That’s interesting, I can’t find any examples of this in the reagent or react docs, do you have a link to an example please?

p-himik 2024-02-02T20:52:32.776789Z

No clue. It's just a combination of two things - React refs and the fact that an event handler can be an object with a handleEvent property.

p-himik 2024-02-02T20:54:54.644729Z

Probably something like this (haven't tested):

(defn my-component [{:keys [on-scroll-end]}]
  (let [handler #js {:handleEvent on-scroll-end}]
    (r/create-class
      {:display-name "my-component"
       :component-did-update (fn [this & _] (set! handler -handleEvent (:on-scroll-end (r/props this))))
       :reagent-render (fn [_] ...)})))

jcb 2024-02-02T21:24:31.423329Z

Thank you, I was really just referring to the eventhandler object as everything I see suggests a function . I’ll take a look at this cheers

p-himik 2024-02-02T21:26:19.082779Z

Yeah, it's documented here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#listener

👍🏽 1