om.next其他组件状态的mutate不会导致其他组件重新呈现

时间:2016-09-04 19:09:11

标签: clojurescript om.next

我正在更新我的一个突变中的状态,并且该部分没有使用它的一部分,而是另一部分。当我执行mutate时,我看到app-state在repl中更新,如果我因为其他原因导致组件重新渲染,它将正确显示,但是我无法通过mutate来调度re-渲染第二个组件。在下面的示例中,单击按钮应该减少第二个列表中颜色名称附近的值,但它不会。

有一些示例显示在mutate返回中使用:value [kk],但那些抛出错误,必须是过时的教程,因为当前格式为:value {:keys [...]},所以说代码和一些教程。但是,我无法找到om.next的任何部分实际使用:键作为关键字,不是结构操作(因此不使用:键作为实际关键字,但它是一个常用词,所以我可能有错过了一个地方)

在repl中我看到这个app-state:

=> (om/app-state reconciler)
#object [cljs.core.Atom {:val 
  {:tiles [[:tile/by-pos "a7"]
           [:tile/by-pos "a9"]
           [:tile/by-pos "a11"]],
   :inventory [[:inv/by-color "red"]
               [:inv/by-color "blue"]
               [:inv/by-color "green"]],
   :tile/by-pos {"a7" {:pos "a7", :color nil},
                 "a9" {:pos "a9", :color nil},
                 "a11" {:pos "a11", :color nil}},
   :inv/by-color {"red" {:color "red", :remaining 2},
                  "blue" {:color "blue", :remaining 1},
                  "green" {:color "green", :remaining 1}}}}]

我错过了什么?

(ns omnexttest.core
  (:require [goog.dom :as gdom]
            [om.next :as om :refer-macros [defui]]
            [om.dom :as dom]))

(defmulti read om/dispatch)

(defmethod read :default
    [{:keys [state] :as env} key params]
      (let [st @state ]
            (if-let [[_ value] (find st key)]
                    {:value value}
                    {:value :not-found})))

(defmethod read :tiles
    [{:keys [state] :as env} key params]
     {:value (into [] (map #(get-in @state %) (get @state key))) })

(defmethod read :inventory
    [{:keys [state] :as env} key params]
     {:value (into [] (map #(get-in @state %) (get @state key))) })

(defmulti mutate om/dispatch)

(defmethod mutate 'draw/edit-edge
  [{:keys [state] :as env} _ {:keys [this pos color]}]
    {:value {:keys [[:inv/by-color color :remaining]]}
     :action (fn []  (do
               (swap! state assoc-in [:tile/by-pos pos :color] color )
               (swap! state update-in [:inv/by-color color :remaining] dec)))})

(defn hex-color
  [ this pos color ]
    (om/transact! this `[(draw/edit-edge ~{:this this :pos pos :color color})]))

(defui TileView
    static om/Ident
    (ident [this {:keys [pos]}] [:tile/by-pos pos])
    static om/IQuery
    (query [this] '[:pos :color])
    Object
    (render [this]
      (let [{:keys [pos color] :as props} (om/props this)]
          (dom/li nil
            (str pos " " color)
            (for [color ["red" "green" "blue"]]
              (dom/button #js { :onClick (fn [e] (hex-color this pos color)) }
                       color))))))

(def tile-view (om/factory TileView {:keyfn :pos}))

(defui InvView
    static om/Ident
    (ident [this {:keys [color]}] [:inv/by-color color])
    static om/IQuery
    (query [this] '[:color :remaining])
    Object
    (render [this]
      (let [{:keys [color remaining] :as props} (om/props this) ]
        (dom/li nil (str color " " remaining)))))

(def inv-view (om/factory InvView {:keyfn :color}))

(def app-state {
                      :tiles [{ :pos "a7"  :color nil }
                              { :pos "a9"  :color nil }
                              { :pos "a11" :color nil }
                              ]
                      :inventory [{ :color "red" :remaining 2}
                                  { :color "blue" :remaining 1}
                                  { :color "green" :remaining 1}]
                      })

(defui MapView
       static om/IQuery
       (query [this]
              [{:tiles (om/get-query TileView)}
               {:inventory (om/get-query InvView) }])
       Object
       (render [this]
               (let [tiles (-> this om/props :tiles)
                     inv (-> this om/props :inventory) ]
                (dom/div nil
                  (dom/ul nil
                   (mapv tile-view tiles))
                  (dom/ul nil
                   (mapv inv-view inv))))))

(def reconciler
  (om/reconciler
    {:state app-state
     :parser (om/parser {:read read :mutate mutate})}))

(om/add-root! reconciler
  MapView (gdom/getElement "map"))

(defn on-js-reload []
  ;; optionally touch your app-state to force rerendering depending on
  ;; your application
  ;; (swap! app-state update-in [:__figwheel_counter] inc)
)

1 个答案:

答案 0 :(得分:3)

传递到this的{​​{1}}对于重新呈现非常重要,因此如果om/transact!用于this组件,则所有三个组件都将重新呈现渲染。您可以在MapView中使用该功能(因此使用MapView MapView's),但可以从this调用该功能。在TileView TileView's中你需要这样的东西:

render

当您致电{:keys [click-cb-fn]} (om/get-computed this) 时,您从作为第一个参数传递的组件向下完成重新渲染 - om/transact!。因此,为了将其发挥到极致,如果所有this都是从根组件完成的,并且所有函数都通过计算道具向下传递,那么您将永远不会重新渲染问题。

但是你不必通过功能。另一种方法是将它们保持在触发按钮所在的同一组件上,而是向下传递(再次通过计算出的道具)父组件om/transacts!。重要的是this的第一个参数是什么组件 - 从你喜欢的地方调用om/transact!

在考虑重新渲染时,跟随读取是另一件需要考虑的事情,但不是您给出的示例 - 当需要重新渲染的组件位于渲染树的不同子分支中时,最好考虑它们,使用共同的根om/transact!是不切实际的。

另一件值得注意的事情是变异的this只是用于文档'。所以无论你放在哪里都没有效果。