Fork me on GitHub
#om
<
2017-05-28
>
sophiago04:05:21

i was going to wait until tomorrow to ask this, but while i'm on here troubleshooting something...is there a best practice with Om.Next for querying the DOM after components mount? i.e. code you'd put inside componentDidMount if using straight React? particularly, i need the context of canvas elements and it doesn't seem to be working if i created it and call (.getContext (.getElementById ...) ...) in the same function, which makes sense. but i'm not sure i understand how the reconciler works well enough to know for sure

r0man07:05:56

david, thx for the answer

dvingo11:05:36

sophiago: usually for canvas you can render the canvas element in render and attach a ref to it: https://facebook.github.io/react/docs/refs-and-the-dom.html

sophiago21:05:06

dvingo: hey, i remember meeting you at the NYC Clojure meetup! i'm unclear what you're suggesting here, though. i know how to do this using the React API, but am wondering how to reconcile that (sorry for the pun) with Om.Next. iow, would it be enough to just update props in one function in order to render the canvas and then grab the context in another (as opposed to doing it in one shot inside a do block)?

dvingo00:05:03

oh yea, hey!

dvingo00:05:09

i just put tried this out

dvingo00:05:19

(defui CanvasExample
       Object
       (componentDidMount [this]
                          (let [canvas (aget this "canvas")
                                ctx (.getContext canvas "2d")
                                _ (aset this "ctx" ctx)]
                            (aset ctx "fillStyle" "green")
                            (-> ctx
                                (.fillRect 10 10 100 100))))
  (render [this]
    (dom/canvas #js {:ref (assign-ref this "canvas")})))

sophiago00:05:57

oh so you did actually mean calling the componentDidMount directly

dvingo00:05:04

yep, it's all just react at the end of the day 🙂

dvingo00:05:12

om really only changes how data is managed

sophiago00:05:33

the issue, though, is i'm using a typical factory pattern and just updating a global map. so currently i have a function like:

(defn new-canvas [id width height]
  (let [str-id (str id)
        key-id (keyword id)]
    (swap! store assoc key-id
           {:id str-id, :width width, :height height})
    (swap! store assoc-in [key-id :ctx]
           (.getContext
            (.getElementById js/document (str id)) "2d"))
    id))

sophiago00:05:47

it seems to be creating a race condition (again, not totally sure when the reconciler triggers rerenders) where the canvas hasn't been rendered for me to pull the ctx

dvingo00:05:29

i see, yea this is outside of react's model

sophiago00:05:43

actually, i suppose i could just modify the original defui to pull the context in componentDidMount and add it to my global map. that seems much cleaner

dvingo00:05:03

was about to say the same 🙂

dvingo00:05:36

i just store the ctx on the component itself, but for sure you can add it into the store as well

dvingo00:05:37

i often use this helper for setting the ref btw

sophiago00:05:39

yeah, i'd need to have it in the store since i'm drawing from outside the component itself

dvingo00:05:42

(defn assign-ref [this ref]
  (fn [el]
    (aset this ref el)))

dvingo00:05:04

you used to be able to pass a string but now the recommendation is to use a callback,

sophiago00:05:41

i'm a bit confused by this. both the purpose and what aset does in cljs

dvingo00:05:41

cool, that makes sense

dvingo00:05:34

the oops readme has a good explanation

sophiago00:05:06

ok, so this is just a way to cut down on the js boilerplate when you need to set a var to a ref queried from the dom?

sophiago00:05:22

oh wow i had no idea there was a cljs specific cheatsheet 🙂

dvingo00:05:37

yea, i use it in plain js too

dvingo00:05:52

haha yea, i keep finding things scattered about the net

sophiago00:05:52

it's still odd to me why you'd need a js array

dvingo00:05:06

it's poorly named, because JS arrays are really objects anyway, you can use aset for objects

dvingo00:05:19

it will compile to obj["prop"] = val

sophiago00:05:35

ah true. it's been a while since i've written plain js (thankfully)

sophiago00:05:59

well, thanks for the tips! i may have to wait to try them out...this is all on another branch of this project i realized afterwards i could maybe debug without using multiple canvases and just one hardcoded one. not sure which i'll end up using

dvingo00:05:43

cool, no problem and good luck!

sophiago02:05:22

hey, just hoping for a sanity check on modifying your code to store the context in the map (similar to how the keys are initialized, rather than directly with swap!):

(defui canvas
  Object
  (render [this]
          (let [{:keys [id width height]} (om/props this)]
            (dom/input #js
                       {:id id
                        :width width
                        :height height
                        })))
  (componentDidMount [this]
                     (let [canvas (aget this id)
                           ctx (.getContext canvas "2d")
                           {:ctx ctx} (om/props this)])))

dvingo00:05:47

hey, just saw your message - i don't follow this code, did it end up working?

dvingo00:05:53

I don't see how the ctx in your example is getting passed down

sophiago03:05:52

i actually had a really full day so didn't get to work on this. your code made sense just from the snippet, but the difference is i want to have the context in the store since i'm drawing to it from regular cljs functions (i'm trying to refactor a project that currently uses a hardcoded canvas because i realized it made more sense to use several of them generated on the fly)

sophiago03:05:29

perhaps i should just call swap! inside componentDidMount? i figured i could maybe assoc it through however the defui macro assocs the keys, but am not sure if that even works slash what the correct syntax would be to do it

dvingo12:05:28

hey, seems like the confusion is mainly around the om way of doing things. it will probably help a lot to go through the om tutorials. tony kay (of untangled) has a really helpful one: https://github.com/awkay/om-tutorial

sophiago21:05:42

Thanks, I'll look into it. I think I've been using Om in a kind of hacky way so far, especially since I usually want to integrate it into a larger library. Like in another version of this library I just call swap! on the store with data for my one key and it creates new elements from a factory. It works, but surely can't be the ideal way of doing things and now that I'm trying something more complicated it's creating trouble.

dvingo11:05:44

then grab the ref in didmount or did update

sova-soars-the-sora16:05:04

Also, just a general clojurescript gotcha is the (-.javascriptstuff ... ... ...)

pedroteixeira17:05:34

trying on master currently see the following error

➜  om git:(master) lein cljsbuild once hello
Compiling ClojureScript...
Compiling "examples/hello/main.js" from ["src" "examples/hello/src"]...
Compiling "examples/hello/main.js" failed.
clojure.lang.ExceptionInfo: failed compiling file:examples/hello/out/cljs/pprint.cljs {:file #object[java.io.File 0x1bff5ad0 "examples/hello/out/cljs/pprint.cljs"]}
...
Caused by: clojure.lang.ExceptionInfo: Invalid :refer, macro cljs.pprint/deftype does not exist in file examples/hello/out/cljs/pprint.cljs {:tag :cljs/analysis-error}

pedroteixeira17:05:37

it does not seem specific to Om, but wanted to check here first

pedroteixeira21:05:31

no, but boot devcards is working. i posted on #clojurescript about the 'deftype does not exist'