Reputation: 13425
I'm trying to get my head around some CLJS and Reagent, and I'm running into an issue when I try to apply one update or another to an atom.
I have an increment function, incDieCount
, which increments the value for a particular key in the map. I've tried to write a function which should decrement the value for a key as well. It works if the value is 0 (it won't decrement below that), but instead of decrementing the current value by one, it always sets the value to zero. What am I missing?
(defonce app-state
(reagent/atom
{:dice
{:d4 0
:d6 0
:d8 0
:d10 0
:d12 0
:d20 0
:d100 0}}))
(defn incDieCount [die]
#(swap! app-state update-in [:dice die] inc))
(defn decDieCount [die]
(let [count (get-in app-state [:dice die])]
(if (> 0 count)
#(swap! app-state update-in [:dice die] dec)
#(swap! app-state assoc-in [:dice die] 0))))
(defn diceEl [[die count]]
^{:key die} [:li
(str (name die) ": " count)
[:button {:on-click (incDieCount die)}
"Add"]
[:button {:on-click (decDieCount die)}
"Subtract"]])
(defn page [ratom]
[:ul
(for [tuple (:dice @ratom)] (diceEl tuple))])
(defn reload []
(reagent/render [page app-state]
(.getElementById js/document "app")))
(defn ^:export main []
(dev-setup)
(reload))
Upvotes: 1
Views: 593
Reputation: 5395
To add to @Ming's answer: first, you need (> count 0)
instead of (> 0 count)
- the latter translates to count < 0
.
Second, it's not recommended to work with atoms non-atomically - in your decDieCount
code, the condition count > 0
is checked when the component is rendered, not when the button is clicked (what if the value of the dice changes in-between?)
It would be better to rewrite decDieCount
as follows:
(defn decDieCount [die]
(fn []
(swap! app-state update-in [:dice die]
#(if (pos? %) (dec %) 0))))
This way you are guaranteed that the new value of the die is based on its current value.
Upvotes: 8
Reputation: 4300
To get an atom's current value, you need to dereference it: @app-state
.
(let [count (get-in @app-state [:dice die])] ...)
Upvotes: 1