om.next мутирует состояние другого компонента, не вызывая повторный рендеринг другого компонента

Я обновляю состояние в одной из моих мутаций, и часть этого не используется этим компонентом, но другой. Когда я выполняю преобразование, я вижу, что состояние app обновляется в repl, и если я заставляю компонент повторно выполнять рендеринг по другим причинам, он будет отображаться правильно, но я не могу заставить mutate запланировать повторное выполнение. визуализация второго компонента. В приведенном ниже примере нажатие на кнопку должно уменьшить значение рядом с именем цвета во втором списке, но это не так.

Есть несколько примеров, показывающих использование:value [k k] в возвращении mutate, но те, которые выдают ошибку, должны быть устаревшими учебниками, так как текущий формат:value {:keys [...]}, так говорит код и некоторые учебники. Однако я не могу найти какую-либо часть om.next, на самом деле ИСПОЛЬЗУЮЩУЮ: ключи как ключевое слово, которое не является операцией деструктуры (поэтому я не использую: ключи как фактическое ключевое слово, но это обычное слово, поэтому я мог где-то пропустить)

В ответе я вижу это для состояния приложения:

=> (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 ответ

Решение

this что передается в om/transact! важно для повторного рендеринга, поэтому здесь, если this был для MapView компонент тогда все три компонента будут перерисованы. Вы можете иметь функцию в MapView (таким образом, используя MapView'sthis) но позвони TileView, В TileView'srender вам нужно что-то вроде этого:

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

Когда вы звоните om/transact! повторный рендеринг выполняется из компонента, который вы передаете в качестве первого аргумента - this, Таким образом, чтобы довести это до крайности, у вас никогда не будет проблем с повторным рендерингом, если все om/transacts!Это делается из корневого компонента, и все функции передаются через вычисленные реквизиты.

Но вам не нужно передавать функции вниз. Альтернатива состоит в том, чтобы сохранить их в том же компоненте, где находится кнопка запуска, и вместо этого передать (снова через вычисленные реквизиты) родительский компонент this, Все, что имеет значение, это то, что является первым аргументом om/transact! это - вызов om/transact! откуда угодно

Последующие операции чтения - это еще одна вещь, которую следует учитывать при размышлении о повторном рендеринге, но не для приведенного вами примера - их лучше всего рассматривать, когда компонент, который необходимо перерисовать, находится в другой ветке дерева рендеринга, где используется общий корень this не будет практичным.

Еще одна вещь, которую стоит отметить, это то, что value это "только для документации". Так что все, что вы положите туда, не будет иметь никакого эффекта.

Другие вопросы по тегам