Kevin Whitaker
Kevin Whitaker

Reputation: 13425

Clojurescript: How to conditionally update a hash map?

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

Answers (2)

Aleph Aleph
Aleph Aleph

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

Ming
Ming

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

Related Questions