Fork me on GitHub
#reagent
<
2020-04-04
>
andrewboltachev15:04:19

Hello. When using reagent components (say, I'm using the form with r/create-class), how to bind custom event handlers (e.g. I have :clickHandler (fn [event] (this-as t t)) and want this to be the element here, not Window)? Should I do (.bind ...) on :component-did-mount or in some other place?

p-himik15:04:16

Don't know what the rest of your code looks like, but if possible I would use ref instead of this.

lilactown15:04:07

what do you want to do in your click handler that requires this?

juhoteperi16:04:58

Usually there is no reason to make the event handlers class methods with Reagent, like is common in React. Just create handler fn in let binding. Anyway, the first parameter of class methods is going to be the component instance. Then you can ask the dom node from that. (This doesn't work for custom methods, just built-in methods)

juhoteperi16:04:40

And using ref instead of checking component instance for dom node is highly recommended by React now.

p-himik16:04:16

Yeah, was just about to quote it: "If you have absolutely no control over the child component implementation, your last option is to use findDOMNode(), but it is discouraged and deprecated in StrictMode [in favor of refs]."

andrewboltachev16:04:21

@U4YGF4NGM i.e. when a menu isn't open, then don't close it. I thought that as a way to avoid menu instantly closing on my click event

andrewboltachev16:04:29

@U061V0GG2 @U2FRKM4TW so that should be function refs right? I.e. not React.createRef() but sth more old?

lilactown16:04:07

is there a reason you’re use r/create-class?

p-himik16:04:10

You can use createRef if it would work in your case. Hard to say anything based on a general description.

lilactown16:04:23

I don’t think you want to use refs here

andrewboltachev16:04:57

well. what I want is that when I click outside of menu it closes (also possibly with CSSTransition from react-transition-group)

andrewboltachev16:04:37

and lifecycle methods I need to add/remove click event listener on whole document element

lilactown16:04:57

gotcha. that makes sense

lilactown16:04:31

like juhoteperi said, you should create the handler in a let binding before you create the class. then pass in the props you need to give to the click handler in the reagent-render function

(defn my-component
  (let [click-handler (fn [event thing1 thing2]
                        ;; do stuff with event and thing1 and thing2
                        )]
    (r/create-class
      {:reagent-render
       (fn [thing1 thing2]
         [:button {:on-click #(click-handler % thing1 thing2)} "click me"])})))

andrewboltachev16:04:34

well, but that's a click handler for whole document, not for a button

lilactown16:04:03

I’m talking about your original question, about how to access this inside an event handler to get props

andrewboltachev16:04:51

thanks for suggestion, but yes, I wanted to get props inside of a handler for click on document

lilactown16:04:37

component-did-mount and component-will-unmount both receive this as an argument

andrewboltachev16:04:52

yes, I tried that, but then it somehow didn't work

andrewboltachev16:04:10

`(js/document.addEventListener "click" (.bind (aget this "clickHandler") this))`

andrewboltachev16:04:49

and clickHandler still received event as first arg, and Window as this

andrewboltachev16:04:15

(the way to get this in a 1st place is (this-as t t))

lilactown16:04:03

lifecycle handlers inside r/create-class get passed this as the first argument

andrewboltachev16:04:23

that's how I got it

andrewboltachev16:04:38

(js/document.addEventListener
"click"
(.bind (aget this "clickHandler") this))

andrewboltachev16:04:57

this code is inside of my :component-did-mount

lilactown16:04:03

sorry, I was confused by what you said using this-as. you mean you’re using this-as inside the clickHandler

andrewboltachev16:04:27

yes, I did use this-as in a clickHandler

lilactown16:04:21

(let [click-handler (fn [component event]
                      (let [props (r/props component)]
                        ,,,))]
  (r/create-class
   {:component-did-mount
    (fn [this]
      (let [bound-click-handler #(click-handler this %)]
        (set! (.-clickHandler this) bound-click-handler)
        (js/document.addEventListener
         "click"
         bound-click-handler)))
     :component-will-unmount
     (fn [this]
       (js/document.removeEventListener
        "click"
        (.-clickHandler this)))
     ,,,}))

andrewboltachev17:04:01

yes, that works!

andrewboltachev17:04:42

perhaps the last thing would be remove'ing the correct function

andrewboltachev17:04:04

i.e. wrapped one

lilactown17:04:30

it should be removing the correct one

lilactown17:04:45

that’s why I (set! (.-clickHandler this) bound-click-handler)

lilactown17:04:07

and then remove (.-clickHandler this) in component-will-unmount

andrewboltachev17:04:36

yes sure! so the only "mystery" here is why (.bind ...) didn't work

lilactown17:04:58

I’m not sure, I’d have to see the rest of the code you had. this can get quirky when mixing react/reagent/JS code

andrewboltachev17:04:41

ok good to know. thanks for help @U4YGF4NGM!

andrewboltachev17:04:53

(but I think I'll also add transitions)

tstirrat21:04:40

i'm a react developer, and i'm just starting to dip my feet into the world of clojure

tstirrat21:04:56

how is the interop between JS/React libs and cljs-specific libs?

tstirrat21:04:31

is there a preferred approach between using clojure-specific libs vs js-specific libs to solve a given problem?

tstirrat21:04:17

routing's an example - I know and like react-router. does it make sense to figure out how to use it in my reagent project, or is it better to come at it from the direction of clojure (e.g. using reitit and accountant)

lilactown21:04:38

interop can be a bit difficult. a lot of people opt for something that controls routing outside of react, like reitit and accountant

tstirrat21:04:01

got it. i guess i'll bias towards a clojure-forward approach for the meantime.