3

我正在更新我的一个突变中的状态,其中一部分未被此组件使用,但被另一个组件使用。当我执行 mutate 时,我看到应用程序状态在 repl 中更新,如果我导致组件由于其他原因重新渲染,它将正确显示,但我无法让 mutate 安排重新第二个组件的渲染。在下面的示例中,单击按钮应该减少第二个列表中颜色名称附近的值,但事实并非如此。

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

在 repl 我看到这个应用程序状态:

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

1 回答 1

3

this传入的om/transact!对重​​新渲染很重要,所以这里如果this是一个MapView组件,那么所有三个组件都将被重新渲染。您可以在MapView(因此使用MapView's this)中拥有该功能,但从TileView. 在TileView's render你需要这样的东西:

{:keys [click-cb-fn]} (om/get-computed this)

当您调用om/transact!时,从您作为第一个参数传递的组件向下完成重新渲染 - thisom/transacts!因此,为了将其发挥到极致,如果所有s 都是从根组件完成的,并且所有函数都通过计算的 props 向下传递,那么您将永远不会遇到重新渲染问题。

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

在考虑重新渲染时,后续读取是要考虑的另一件事,但不适用于您给出的示例 - 当您需要重新渲染的组件位于渲染树的不同子分支中时,最好考虑它们,其中使用一个共同的根this是不切实际的。

另一件需要注意的是,变异value是“仅用于文档”。所以无论你放在那里都没有效果。

于 2016-09-09T14:07:53.053 回答