使用 Clojure/Reagent 制作一个简单的倒数计时器
Making a simple countdown timer with Clojure/Reagent
我正在尝试用几乎没有经验的 Clojure 和 Reagent,尝试制作一个简单的计时器。
(defn reset-component [t]
[:input {:type "button" :value "Reset"
:on-click #(reset! t 60)}])
(defn countdown-component []
(let [seconds-left (atom 60)]
(fn []
(js/setTimeout #(swap! seconds-left dec) 1000)
[:div.timer
[:div "Time Remaining: " (show-time @seconds-left)]
[reset-component seconds-left]])))
在我按下重置按钮之前,计时器倒计时似乎正常工作。之后,计时器开始以两倍的速度倒计时。每次我按下重置按钮时,倒计时都会更快。
如何让计时器在页面加载时自动倒计时,而不是在单击重置按钮时倒计时更快?
取消引用 reset-component 中的 seconds-left 会触发 countdown 组件的重新渲染,这会将另一个递减器函数附加到您的 countdown-component。
你可能想要这个:
(defn reset-component [t]
[:input {:type "button" :value "Reset"
:on-click #(reset! t 60)}])
(defn countdown-component []
(let [seconds-left (atom 60)]
(js/setInterval #(swap! seconds-left dec) 1000)
(fn []
[:div.timer
[:div "Time Remaining: " (show-time @seconds-left)]
[reset-component seconds-left]])))
请注意,现在对 setInterval
的调用仅在组件初始化时发生,而不是在渲染阶段发生。
我找到的解决方案是在组件函数之前使用setInterval
而不是在组件函数内部使用setTimeout
:
(defn reset-component [seconds]
[:input {:type "button" :value "Reset"
:on-click #(reset! seconds 60)}])
(defn countdown-component []
(let [seconds-left (atom 60)]
(js/setInterval #(swap! seconds-left dec) 1000)
(fn []
[:div.timer
[:div "Time Remaining: " @seconds-left]
[reset-component seconds-left]])))
为了将来参考,reagent
0.6.0+ 附带了一个新的 with-let
宏,对于需要适当 "destruction" 的东西非常有用,例如 setInterval
/clearInterval
:
(defn countdown-component []
(r/with-let [seconds-left (r/atom 60)
timer-fn (js/setInterval #(swap! seconds-left dec) 1000)]
[:div.timer
[:div "Time Remaining: " (str @seconds-left)]]
(finally (js/clearInterval timer-fn))))
这是该功能的官方 anouncement。
请注意,从渲染函数调用 setTimeout
也可以,但不被视为良好做法。 (例如 https://clojurians.slack.com/archives/C0620C0C8/p1495568238109060)
我正在尝试用几乎没有经验的 Clojure 和 Reagent,尝试制作一个简单的计时器。
(defn reset-component [t]
[:input {:type "button" :value "Reset"
:on-click #(reset! t 60)}])
(defn countdown-component []
(let [seconds-left (atom 60)]
(fn []
(js/setTimeout #(swap! seconds-left dec) 1000)
[:div.timer
[:div "Time Remaining: " (show-time @seconds-left)]
[reset-component seconds-left]])))
在我按下重置按钮之前,计时器倒计时似乎正常工作。之后,计时器开始以两倍的速度倒计时。每次我按下重置按钮时,倒计时都会更快。
如何让计时器在页面加载时自动倒计时,而不是在单击重置按钮时倒计时更快?
取消引用 reset-component 中的 seconds-left 会触发 countdown 组件的重新渲染,这会将另一个递减器函数附加到您的 countdown-component。
你可能想要这个:
(defn reset-component [t]
[:input {:type "button" :value "Reset"
:on-click #(reset! t 60)}])
(defn countdown-component []
(let [seconds-left (atom 60)]
(js/setInterval #(swap! seconds-left dec) 1000)
(fn []
[:div.timer
[:div "Time Remaining: " (show-time @seconds-left)]
[reset-component seconds-left]])))
请注意,现在对 setInterval
的调用仅在组件初始化时发生,而不是在渲染阶段发生。
我找到的解决方案是在组件函数之前使用setInterval
而不是在组件函数内部使用setTimeout
:
(defn reset-component [seconds]
[:input {:type "button" :value "Reset"
:on-click #(reset! seconds 60)}])
(defn countdown-component []
(let [seconds-left (atom 60)]
(js/setInterval #(swap! seconds-left dec) 1000)
(fn []
[:div.timer
[:div "Time Remaining: " @seconds-left]
[reset-component seconds-left]])))
为了将来参考,reagent
0.6.0+ 附带了一个新的 with-let
宏,对于需要适当 "destruction" 的东西非常有用,例如 setInterval
/clearInterval
:
(defn countdown-component []
(r/with-let [seconds-left (r/atom 60)
timer-fn (js/setInterval #(swap! seconds-left dec) 1000)]
[:div.timer
[:div "Time Remaining: " (str @seconds-left)]]
(finally (js/clearInterval timer-fn))))
这是该功能的官方 anouncement。
请注意,从渲染函数调用 setTimeout
也可以,但不被视为良好做法。 (例如 https://clojurians.slack.com/archives/C0620C0C8/p1495568238109060)