Fork me on GitHub
#clojurescript
<
2021-09-13
>
maverick04:09:35

(defn do-layout []
      (hash/watch)
      (let []
           (fn []
               (main-panel/get-list)
               )))

(defn ^:export run []
  (r/render [do-layout] (js/document.getElementById "app")))
I am trying to call get api at time of app load in reagent. But it is being called infinite times. I just want to call it once at time of page load.

Fredrik04:09:12

Can you share the code using the get api?

thheller05:09:56

you might want to follow the recommend pattern of init/start/stop described here

thheller05:09:20

so run becomes start and you add an init that does your get api and calls start

thheller05:09:31

and in your build config you call init instead of run

zendevil.eth05:09:23

can someone explain to me why the following doesn’t work in reagent?

(let [elem (.getElementById js/document "foo")
      _ (prn "elem is" elem)]
    [:div {:id "foo"}])
prints “elem is nil”

Fredrik05:09:15

Do you have an element with id "foo" in your html file?

Fredrik05:09:16

That element must be present when (.getElementById js/document "foo") executes

zendevil.eth05:09:22

the div with that id is right in the code

Fredrik05:09:35

But the div is not present in the dom when it is looking for it

Fredrik05:09:48

It is created later

Fredrik05:09:42

It is like a function referencing a variable before that variable is even declared

lilactown05:09:23

Frederik is right. your getElementById runs when your component is rendered, but the DOM node will not be created until later by React

lilactown05:09:52

the correct way to do this is with a React ref: https://reactjs.org/docs/refs-and-the-dom.html

lilactown05:09:56

i.e.

[:div {:ref #(when (some? %)
               (prn "elem is" %))}]

zendevil.eth05:09:47

and how to make it work?

thheller05:09:27

@ps in any react-based app the "render" only describes what your DOM will look like at a future point it time. it does not immediately construct it, so in your case #foo does not exist when you try to get it. so to access the DOM by regular means you need to use the proper lifecylce hooks (eg. useEffect). if you just want access to a DOM element refs might be enough but they also will require using the lifecycle hooks before you can interact with them.

zendevil.eth06:09:37

I tried to convert the following THREE.js code to cljs but I don’t really see the animation: https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene Any idea why that might be?

(defn landing []
  (let [mount (atom nil)
        scene (js/THREE.Scene.)
        _ (js/console.log "scene is " scene)
        camera (js/THREE.PerspectiveCamera.
                    75 (/ (.-innerWidth js/window)
                          (.-innerHeight js/window))
                       0.1
                       1000)
        _ (js/console.log "camera is " camera)
        renderer (js/THREE.WebGLRenderer.)
        _ (.setSize renderer (.-innerWidth js/window) 
                    (.-innerHeight js/window))
        _ (js/console.log "renderer is " renderer)
        _ (try (.. @mount 
                 (appendChild (.-domElement renderer)))
               (catch js/Object e (prn e @mount)))
        geometry (js/THREE.BoxGeometry.)
        _ (js/console.log "geometry is " geometry)
        material (js/THREE.MeshBasicMaterial. 
                      (clj->js {:color 0xff0000}))
        _ (js/console.log "material is " material)
        cube (js/THREE.Mesh. geometry material) 
        _ (js/console.log "cube is " cube)
        _ (.add scene cube)
        _ (set! (.. camera -position -z) 5)
        _ (prn "trying foo")]
        
                  
    (defn animate []
      (.requestAnimationFrame js/window animate)
      (set! (.. cube -rotation -x)
            (inc (.. cube -rotation -x)))
      (set! (.. cube -rotation -y)
            (inc (.. cube -rotation -y)))
      (.render renderer scene camera))
    (animate)
    [:div
     [:div.hero {:ref (fn [ref] (reset! mount ref))}]]))

thheller06:09:12

same problem again. ignoring the lifecycle and trying to do stuff too early. also don't defn inside a defn.

zendevil.eth06:09:50

nested defn’s aside, now I have the following but it still doesn’t work:

(defn landing []
  (let [[renderer set-renderer] (useState nil)
        [cube set-cube] (useState nil)
        [camera set-camera] (useState nil)
        [scene set-scene] (useState nil)
        mount (atom nil)
        _ (useEffect 
            (fn []
              (let [_ (set-scene (js/THREE.Scene.))
                    _ (js/console.log "scene is " scene)
                    _ (set-camera 
                        (js/THREE.PerspectiveCamera.
                                       75 (/ (.-innerWidth js/window)
                                             (.-innerHeight js/window))
                                          0.1
                                          1000))
                    _ (js/console.log "camera is " camera)
                    _ (set-renderer (js/THREE.WebGLRenderer.))
                    _ (.setSize renderer (.-innerWidth js/window 
                                          (.-innerHeight js/window)))
                    _ (js/console.log "renderer is " renderer)
                    _ (try (.. @mount 
                             (appendChild (.-domElement renderer)))
                           (catch js/Object e (prn e @mount)))
                    _ (set-geometry (js/THREE.BoxGeometry.))
                    _ (js/console.log "geometry is " geometry)
                    _ (set-material (js/THREE.MeshBasicMaterial. 
                                      (clj->js {:color 0xff0000})))
                    _ (js/console.log "material is " material)
                    _ (set-cube (js/THREE.Mesh. geometry material)) 
                    _ (js/console.log "cube is " cube)
                    _ (.add scene cube)
                    _ (set! (.. camera -position -z) 5)
                    _ (prn "trying foo")])))]
                       
                  
    (defn animate []
      ;;(prn "running animate")
      ;;(prn "mount is " @mount)
      (.requestAnimationFrame js/window animate)
      (set! (.. cube -rotation -x)
            (inc (.. cube -rotation -x)))
      (set! (.. cube -rotation -y)
            (inc (.. cube -rotation -y)))
      (.render renderer scene camera))
    (animate)
    [:div
     [:div.hero {:ref (fn [ref] (reset! mount ref))}]]))

thheller06:09:52

also just a minor note but if your entire let has only _ bindings then why use a let in the first place 😛

thheller06:09:56

also the animate will throw all over the place since it starts running too early before the useEffect

Johan Thorén06:09:06

I've written a https://github.com/johanthoren/equinox. It has no dependency except Clojure, and there is no Java interop going on. I would like to make this available to both Clojure and ClojureScript, and I have searched quite a while for a good resource on 'how to port a Clojure library to ClojureScript', but I haven't found one. Do you know of a good article describing the steps one needs to take? I don't want to do experimental pushes to Clojars if I can avoid it.

thheller06:09:52

@jthoren its already .cljc so nothing left to do, assuming there is no special other JVM related interop you are done porting 😉

Johan Thorén06:09:33

@thheller, is that really all? So by deploying a new version to Clojars one would be able to import this in a ClojureScript project?

Johan Thorén06:09:15

No dependency to ClojureScript that needs to be added, etc.

thheller06:09:45

nope, all done

Johan Thorén06:09:54

Wow. That's a breeze. Thanks!

thheller06:09:12

but be aware that numbers behave very differntly in JS and therefore CLJS. don't know if that'll become a problem for that code 😉

Johan Thorén06:09:13

I looked through it based on the page detailing the differences and I couldn't see any function that would be affected as far as I could tell.

Johan Thorén06:09:43

I imported it in a hello-world ClojureScript project and the results given are the same as in a Clojure one, so I think I'm good to go. Thanks for the help.

👍 2
zendevil.eth07:09:09

I’m trying to use the following js file: https://github.com/mrdoob/three.js/blob/master/examples/jsm/geometries/DecalGeometry.js And I basically include it in the empty html file that the server sends that my cljs app works upon. But I’m getting this error:

Uncaught SyntaxError: Cannot use import statement outside a module

p-himik07:09:05

That error is very easy to search for on the web. Also, it has nothing to do with CLJS.

Pepijn de Vos13:09:10

What's the point of a Var in cljs? It appears that in clojure a symbol will be resolved at compile time so doing alter-var-root later has no effect, so if you (declare foo) you have to use #'foo to make sure you get the actual current value in Clojure, but as far as I understand in clojurescript def is just a JS variable that you can set! so it seems you can use declare and then just use the symbol and set it later? Is there any other use for #'vars that makes them useful in JS?

Pepijn de Vos11:09:35

Ah so mostly metadata

zendevil.eth13:09:20

does anyone know how to do stuff with the file you get from the [:input {:type :file}]? I couldn’t find it on google. I tried this:

[:input.drop {:type :file 
                      :accept "image/png, image/jpeg, image/svg"
                      :on-change (fn [e] (js/console.log "file is " 
                                                         (.. e -target -value)))}]
But (.. e -target -value) prints the file C:\fakepath…. I want to eventually send the file to the server but also display the image in the browser when it’s uploaded, so am not sure what object to actually send into the multipart request and what object to attach to the dom element

zendevil.eth13:09:05

I don’t understand. I know about the additional attributes. I just want to take one file and use it. I just don’t know what to do with the on-change event to extract the file from it and couldn’t find anything on google

p-himik13:09:00

> A `FileList` object that lists every selected file. This list has no more than one member unless the `multiple` attribute is specified.

p-himik13:09:13

That page has an example that does exactly what you want.

zendevil.eth13:09:15

yeah but (.. e -files) gives undefined

simongray13:09:40

e is the js/Event, you want the target of the event, not the event itself, so (.. e -target -files)

Pepijn de Vos13:09:05

Wait I have a cljs example actually...

Pepijn de Vos13:09:41

(defn open-schematic [e]
  (let [data (.text (aget (.. e -target -files) 0))]
    (.then data (fn [data]
                  (let [parsed (clojure.edn/read-string
                                {:readers {'transform transform}}
                                data)]
                    (if (s/valid? ::schematic parsed)
                      (swap! schematic into parsed) ; TODO rethink
                      (js/alert (s/explain-str ::schematic parsed))))))))

simongray13:09:57

In almost every case you will want the target since it has the value you’re interested in. The event itself is usually not the interesting part in event handlers other than as a proxy to get whatever triggered the event.

zendevil.eth13:09:36

aget is what was tripping me. I was trying nth

p-himik13:09:38

What was tripping you is your wrong usage of -files, just as simongray said. nth works just fine on JS arrays.

zendevil.eth13:09:54

no it wasn’t before when I tried on FileList

p-himik13:09:36

Ah, right, it's not a proper array.

simongray13:09:57

For FileList there is also the .item method

maverick13:09:29

I am calling http-post request but it is failing when I use atom as form-data but when I hardcode the values it is getting success

p-himik13:09:28

Are you deref'ing that atom?

maverick14:09:08

Yes I am doing it.

p-himik14:09:09

Either the data is not in that atom, or there's something really wrong. Can't tell either way without the code.