Fork me on GitHub
#reagent
<
2018-09-07
>
Josh Horwitz03:09:43

Hey all, I’m confused why when I click that button the visible state is not toggling, am I missing something?

justinlee03:09:22

@joshua.d.horwitz yea you need return a function inside your let block

justinlee03:09:45

but the basic answer to why that’s happening is that when you click the button, the visible ratom gets changed, causing the component to re-render, which causes the visible ratom to get re-initialized

justinlee04:09:34

if you return a function instead of a vector, reagent treats that as a special case and only re-runs the returned function (rather than the whole thing). this preserves your let block

kaosko05:09:46

I'm getting into writing macros and perhaps this is more of a question about them than reagent, but why does this work: (defmacro test [& args] [:div "hello word"]) and this doesn't: (defmacro test [& args] `(r/create-class {:reagent-render (fn [_] [:div "hello world"]) })) ? I'm confused

justinlee06:09:26

how does it fail?

kaosko06:09:53

A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.

thomas10:09:07

Hi, I am stuck on a 'normal' react problem (just JS no cljs unfortunately) and I was wondering if there is anyone who might be able to help?

thomas10:09:33

the state of my component updates sometimes... but I can't see how/what/where/when etc.

thomas10:09:40

any ideas on how to debug this?

pesterhazy17:09:13

console.log! 🙂

pesterhazy17:09:03

add logging to the constructor fn and the render fn, possibly unmount as well

pesterhazy17:09:47

think about what can cause a component to rerender (props changes, this.state changes, or explicitly calling render)

jplaza17:09:52

“What” should be simple to find since there are only two ways to alter a component state

pesterhazy17:09:49

React is very logical so the scientific method (and read the React docs) goes a long way

kaosko17:09:34

omg, I just don't understand why this doesn't work: (defmacro with-test [] `(r/create-class {:reagent-render (fn [] [:div "hello world"]) }))

justinlee17:09:57

how are you using it? have you tried using macroexpand to see what you are getting?

kaosko17:09:05

I'm using it just normally (with-test)

kaosko17:09:35

macroexpand hangs in figwheel repl so clearly I'm doing something wrong

kaosko17:09:04

or macroexpand-1

kaosko17:09:28

macroexpand works fine for core functions

justinlee17:09:23

well i’m trying to try it but suddenlly my cursive repl broke so i’ll have to fix that first

lilactown17:09:32

I just tried to start up rebel-readline and it suddenly broke too 🙃

justinlee17:09:39

okay that’s all fixed up

justinlee17:09:55

are you putting your macro in a clj file?

justinlee17:09:02

you can’t use macros in cljs files

kaosko17:09:55

yeah that's in a cljs. and macros need to be defined in a separate namespace? just reading https://cljs.github.io/api/cljs.core/defmacro

justinlee17:09:27

yea that won’t work. the clojurescript compiler is a clojure program, so your macros also have to be clojure

justinlee17:09:20

because i only have a couple of macros, i just create a macros.clj file in my project and dump them there. then i do a (:require-macros [my-ns.macros :refer [my-macro])

justinlee17:09:51

you can do this funky thing to simplify the ns form when you require them but I don’t bother

kaosko17:09:42

yeah that's it, works fine. yeah the docs are there but just have to find the right one...

kaosko18:09:16

makes perfect sense given macros are compile time constructs but I wish something, like figwhel would flag it as an error if you try defmacro in cljs namespace

justinlee18:09:52

yea it really could be more helpful

justinlee18:09:12

it’s actually strange--if it finds a defmacro in a cljs i don’t know why it doesn’t throw a clear error

kaosko18:09:36

nobody thought of newbies like me 🙂

kaosko19:09:34

argh this macro stuff is a quite bit more involving than I thought.. this macro blows up if I put anything in the finally block: (defmacro with-client-rect [client-rect-atom f] `(r/create-class {:component-did-mount (fn [this#] (reset! ~client-rect-atom (common/get-client-rect (r/dom-node this#)))) :reagent-render (fn [] (r/with-let [this# (r/current-component) resize-handler# #(reset! ~client-rect-atom (common/get-client-rect (r/dom-node this#))) _# (.addEventListener js/window "resize" resize-handler#)] [:div "hello world"] (finally ;(println "hello") ;(.removeEventListener js/window "resize" resize-handler#) ))) }))

kaosko19:09:17

looks good in macroexpand, same code in a component function works fine

justinlee19:09:35

not that there’s anything wrong with experimentation, but I have a very strong feeling that you don’t need macros

justinlee19:09:18

for what it is worth, i’m quite surprised that with-let works inside a render function

kaosko19:09:06

I though with-let normally runs in a render function

justinlee19:09:03

i mean i guess it works but the docs introduce it as a kind of substitute for form-2

kaosko19:09:21

yeah the whole issue why I need a form-3 is because I can't use r/dom-node inside r/with-let (because it runs in a render function)

justinlee19:09:36

but back to your problem, i’d suggest composing functionality using react’s component architecture instead of reaching for macros. it’s a nightmare to do things with macros

justinlee19:09:31

like, it looks like you are trying to wrap the resize functionality, so i’d suggest that you do that using your form-3, then pass a render prop or something like that

kaosko19:09:32

I'm noticing 🙂 wanted to try it though because to me it looked like a straight template function case from the outset

justinlee19:09:11

react has really powerful composition built in

lilactown19:09:56

the rule of thumb i have is I only reach for macros when I say, “I wish this had better syntax

kaosko19:09:12

that's what I'm after though

justinlee19:09:37

try just doing it with a normal function

justinlee19:09:42

i’m 99% sure you’ll be able to

justinlee19:09:01

macros are only helpful when you need to prevent evaluation. i don’t think you need that

kaosko19:09:45

yes I know how to do it as a normal function, but it won't be as pretty

lilactown19:09:52

what I try to do then is create a function, call it with-client-rect*, and do all the implementation in there.

kaosko19:09:06

yeah could do that. but for the issue in hand... do I need to do something special for try-catch-final blocks in a macro?

lilactown20:09:21

I don’t know

lilactown20:09:57

I don’t really know how the with-let macro is implemented. it seems like you should be using react lifecycles directly anyway

lilactown20:09:09

since you’re already in a form-3 component

justinlee20:09:51

yea agreed. the with-let is super confusing here. just deal with it in component-will-unmount

kaosko20:09:21

r/with-let is super handy for keeping a reference to the resize handler function so I can clean it up. the lifecycle function don't quite allow that

justinlee20:09:50

just stick it in an atom--that’s what with-let is doing

lilactown20:09:36

or put it on the react class

kaosko20:09:29

hmm put it on the react class, how?

lilactown20:09:32

(r/create-class
 {:component-did-mount
  (fn [this#]
    (set! (.-resizeHandler this#)
          #(reset! ~client-rect-atom (common/get-client-rect (r/dom-node this#))))
    (.resizeHandler this#)
    (.addEventListener js/window "resize" (.-resizeHandler this#)))

  :component-will-unmount
  (fn [this#]
    (.removeEventListener js/window "resize" (.-resizeHandler this#)))

  :reagent-render
  (fn []
    [:div "hello world"])
  })

lilactown20:09:35

something like that

lilactown20:09:20

haven’t tested it, but you can set properties on JS objects like the reference that gets passed into the create-class lifecycles just like in JavaScript

kaosko20:09:31

ah yes, thanks

lilactown20:09:09

you could also use an atom and a closure like justinlee said:

(let [resize-handler (atom nil)]
  (r/create-class
   {:component-did-mount
    (fn [this#]
      (reset! resize-handler
            #(reset! ~client-rect-atom (common/get-client-rect (r/dom-node this#))))
      (@resize-handler)
      (.addEventListener js/window "resize" @resize-handler))

    :component-will-unmount
    (fn [this#]
      (.removeEventListener js/window "resize" @resize-handler))

    :reagent-render
    (fn []
      [:div "hello world"])
    }))

justinlee20:09:26

yea i was thinking of something like:

(defn with-client-rect
  [client-rect-atom child]
  (let [ref (r/atom nil)
        resize-handler (fn [] (reset! client-rect-atom (common/get-client-rect @ref)))]
    (r/create-class
      {:component-did-mount
       (fn [] 
         (resize-handler)
         (.addEventListener js/window "resize" resize-handler))
       :reagent-render
       (into [:div {:ref #(swap! ref %)}]
             child)
       :component-will-unmount
       (.removeEventListener js/window "resize" resize-handler)})))

;[with-client-rect atom [child-node]]

justinlee20:09:41

but maybe i’m missing the kind of syntax you want

kaosko20:09:49

ok thank you both @lee.justin.m and @lilactown. still don't understand why the finally doesn't work - maybe it's the inner macro that throws it off. it's fascinating to me that the macro arguments are passed as unevaluated but having read all the warnings about not using macros too much so I had never tried writing one. so wanted to try it out but I agree, just a plain function will suffice

stvnmllr223:09:20

Hey all, was going to do talk about clojurescript for react folks. Can anyone confirm that they still haven't figured out how to maintain state when saving a file? Like we do with figwheel. I loaded up the create-reacte-app, and it's just reloading the page it seems