Fork me on GitHub

i was going to wait until tomorrow to ask this, but while i'm on here troubleshooting 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


david, thx for the answer


sophiago: usually for canvas you can render the canvas element in render and attach a ref to it:


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)?


oh yea, hey!


i just put tried this out


(defui CanvasExample
       (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")})))


oh so you did actually mean calling the componentDidMount directly


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


om really only changes how data is managed


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]
            (.getElementById js/document (str id)) "2d"))


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


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


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


was about to say the same 🙂


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


i often use this helper for setting the ref btw


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


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


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


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


cool, that makes sense


the oops readme has a good explanation


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?


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


yea, i use it in plain js too


haha yea, i keep finding things scattered about the net


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


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


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


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


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


cool, no problem and good luck!


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
  (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)])))


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


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


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)


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


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:


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.


then grab the ref in didmount or did update


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


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[ 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}


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


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