2016-12-05 51 views
1

我试图让我的头绕过一些CLJS和Reagent,当我尝试将一个更新或另一个更新应用到原子时,我遇到了一个问题。Clojurescript:如何有条件地更新哈希映射?

我有一个增量函数,incDieCount,它增加了地图中特定键的值。我试图写一个函数,它应该减少键的值。它的工作原理是,如果值为0(它不会减少),但不是将当前值减1,而是总是将该值设置为零。我错过了什么?

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

回答

8

要添加到@明的回答是:首先,你需要(> count 0)而不是(> 0 count) - 后者转化为count < 0。其次,不建议非原子操作原子 - 在你的decDieCount代码中,当组件被渲染时,而不是在按钮被单击时检查条件count > 0(如果骰子的值改变, ?

之间)

这将是更好改写 decDieCount如下:

(defn decDieCount [die] 
    (fn [] 
    (swap! app-state update-in [:dice die] 
      #(if (pos? %) (dec %) 0)))) 

这种方式,你都保证了模具的新价值是基于其目前的价值。

+0

这样做,谢谢!一个问题;为什么我需要匿名函数? –

+0

我们在这里有两个匿名函数 - 一个是围绕'die'键做一个闭包(并且你的'incDieCount'代码中有一个类似的匿名函数),第二个函数将'swap!中原子的值变换! ' - 因为你的变换比简单的'inc'或'dec'更复杂(它涉及首先检查计数),所以你需要用自定义函数来表达它。 –

+0

啊,我明白了。谢谢! –

1

要得到一个原子的当前值,你需要取消它的引用:@app-state

(let [count (get-in @app-state [:dice die])] ...)