This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-02-04
Channels
- # announcements (1)
- # architecture (18)
- # aws (7)
- # babashka (63)
- # beginners (38)
- # bristol-clojurians (1)
- # circleci (1)
- # clj-kondo (10)
- # clojars (4)
- # clojure (159)
- # clojure-berlin (3)
- # clojure-europe (4)
- # clojure-italy (7)
- # clojure-losangeles (6)
- # clojure-nl (7)
- # clojure-spec (3)
- # clojure-uk (109)
- # clojurescript (54)
- # css (1)
- # cursive (38)
- # data-science (2)
- # datascript (3)
- # datomic (14)
- # docker (2)
- # duct (11)
- # fulcro (47)
- # jobs (8)
- # jobs-discuss (3)
- # kaocha (4)
- # malli (3)
- # nyc (2)
- # off-topic (30)
- # overtone (3)
- # re-frame (17)
- # reagent (33)
- # shadow-cljs (29)
- # spacemacs (3)
- # specter (4)
- # tools-deps (13)
- # xtdb (13)
How to properly handle nil
checks when using ref
From this example https://github.com/reagent-project/reagent/blob/master/doc/FAQ/UsingRefs.md
(defn video-ui []
(let [!video (clojure.core/atom nil)] ;; stores the
(fn [{:keys [src]}]
[:div
[:div
[:video {:src src
:style {:width 400}
:ref (fn [el]
(reset! !video el))}]]
[:div
[:button {:on-click (fn []
(when-let [video @!video] ;; not nil?
(if (.-paused video)
(.play video)
(.pause video))))}
"Toogle"]]])))
I just noticed that I have to check that @!video
is not nil
everytime I access its properties.I'm thinking if I have to pass the !@video
on other function I have to do the when-let
everytime
So I was thinking if there is a way to just do the check once.
Cool. I like it.
[:div {:class "absolute bg-blue" :width (str (percentage @!audio) "%") }]
Is there a way to get the percentage of else default to 0% here? Sorry to ask, I'm still learning most of the clojure functionsFor what it’s worth, the “ugliness” here can be solved by using https://github.com/mfikes/cljs-bean. That would give you idiomatic keyword access to your JS object, and no need for some->
On my current component. notice I already used 3 when-let
(defn audio-player []
(let [!audio (r/atom nil)]
(fn []
[:div {:class "mx-auto p-16"}
[:div {:class "flex flex-1 items-center p-4 border border-gray-500 rounded-full relative"}
[:div {:class "absolute bg-blue" :width (str (percentage @!audio) "%") }] ;; error since I forgot to check if its not nil
[:button {:class "absolute"
:on-click (fn []
(when-let [audio @!audio]
(if (.-paused audio)
(.play audio)
(.pause audio))))}
"play"]]
[:audio {:src "audios/sample.mp3"
:ref (fn [el]
(reset! !audio el))}]
[:div {:class "flex flex-row flex-1"}
[:div "Duration: " (when-let [audio @!audio] (/ (.-duration @!audio) 60))]
[:div "Current Time: " (when-let [audio @!audio] (.-currentTime @!audio))]
[:button {:on-click (fn [] (backward @!audio 5))} "Back"]
[:button {:on-click (fn [] (forward @!audio 5))} "Forward"]]])))
If I'm not mistaken, with this code you won't get real-time values for the duration and current time. Because when the state of <audio>
changes, there will be nothing that will trigger the re-render.
Hmmmm. This two are being re-rendered. Because they deref the @!audio?
[:div "Duration: " (when-let [audio @!audio] (/ (.-duration @!audio) 60))]
[:div "Current Time: " (when-let [audio @!audio] (.-currentTime @!audio))]
I think !audio
should be changed only once the component is rendered for the first time. Usually, it doesn't change that often.
But if it works fine, then I have no idea why.
I imagine you need to attach some sort of event handlers to the audio player, beyond just capturing the reference, to get the changes to the duration to update your UI
But Jaime said that it was updating even with the code above - that's what puzzles me.
Yes, you guys are right. I need to add the event handler
(defn audio-player []
(let [!audio (r/atom nil)
state (r/atom {:currentTime 0})
time-update-handler #(swap! state assoc :currentTime (.. % -target -value))]
(r/create-class
{:component-did-mount
(fn [this]
(some-> @!audio (.addEventListener "timeupdate" #(swap! state assoc :currentTime (.. % -target -currentTime)))))
:reagent-render
(fn []
[:div {:class "mx-auto p-16"}
[:div {:class "flex flex-1 items-center p-4 border border-gray-500 rounded-full relative"}
[:div {:class "absolute bg-blue" :width (str (if !audio 0 (percentage !audio)) "%")}] ;; error since I forgot to check if its not nil
[:button {:class "absolute"
:on-click (fn []
(when-let [audio @!audio]
(if (.-paused audio)
(.play audio)
(.pause audio))))}
"play"]]
[:audio {:src "audios/sample.mp3"
:ref (fn [el]
(reset! !audio el))}]
[:div {:class "flex flex-row flex-1"}
[:div "Duration: " (when-let [audio @!audio] (.-duration @!audio))]
[:div "Current Time: " (:currentTime @state)]
[:button {:on-click (fn [] (backward @!audio 5))} "Back"]
[:button {:on-click (fn [] (forward @!audio 5))} "Forward"]]])}
)))
It was working before, it updates the Duration even before event handling. Maybe because of when-let? The child was initially nil then ref was set and re-rendered?
[:div "Duration: " (when-let [audio @!audio] (/ (.-duration @!audio) 60))]
Yeah, I can confirm that below gets re-rendered
[:div "Current Time: " (when-let [audio @!audio] (.-currentTime @!audio))]
And this one I do not understand. If it works for some good reason (of which I have no clue), then you don't need any event listeners.
Either way, one might want to track the interesting values of the stateful JS component in a ratom of some sort, for proper reactivity. See this for a good explanation https://github.com/day8/re-frame/blob/master/docs/Using-Stateful-JS-Components.md
Never mind, found https://github.com/alex-dixon/shadow-cljs-react-spring