Почему компонент Om Next не выполняет рендеринг при изменении состояния?

Похоже, этого не происходит, как говорится в руководстве по быстрому старту:

В Om Next изменениями состояния приложения управляет посредник. Реконсификатор принимает новинку, объединяет ее в состояние приложения, находит все затронутые компоненты на основе их объявленных запросов и планирует повторную визуализацию.

Когда я изменяю поле выбора, функция mutate обновляет состояние, но функция рендеринга компонента App никогда не выполняется. С помощью @app-state в REPL я вижу, что состояние изменилось, и я никогда не вижу вывод в консоли от prn в функции рендеринга приложения. Это все, что я вижу в консоли:

 [1955.847s] [om.next] transacted '[(om-tutorial.core/switch-topic {:name "b"})], #uuid "c3ba6741-81ea-4cbb-8db1-e86eec26b540"
"read :default" :topics

Если я обновлю состояние из REPL с (swap! app-state update-in [:current-topic] (fn [] "b")) тогда функция рендеринга приложения выполняется. Вот вывод консоли:

"read :default" :topics
"read :default" :current-topic
"App om-props " {:topics [{:name "a"} {:name "b"}], :current-topic "b"}
"Topics om-props " {:topics [{:name "a"} {:name "b"}]}

Вот полный код:

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

(enable-console-print!)

(def app-state (atom {:current-topic "a" :topics [{:name "a"} {:name "b"}]}))

(defmulti read (fn [env key params] key))

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

(defmulti mutate om/dispatch)

(defmethod mutate 'om-tutorial.core/switch-topic
  [{:keys [state]} _ {:keys [name]}]
  {:action
   (fn []
     (swap! state update-in
       [:current-topic]
       #(identity name)))})

(defui Topics
  static om/IQuery
  (query [this]
         [:topics])
  Object
  (render [this]
          (let [{:keys [topics] :as props} (om/props this)]
            (prn "Topics om-props " props)
            (apply dom/select #js {:id "topics"
                                   :onChange
                                   (fn [e]
                                     (om/transact! this
                                                   `[(switch-topic ~{:name (.. e -target -value)})]))}
                   (map #(dom/option nil (:name %)) topics)))))

(def topics-view (om/factory Topics))

(defui App
  static om/IQuery
  (query [this]
         '[:topics :current-topic])
  Object
  (render [this]
          (let [{:keys [topics current-topic] :as om-props} (om/props this)]
            (prn "App om-props " om-props)
            (dom/div nil
                     (topics-view {:topics topics})
                     (dom/h3 nil current-topic)))))

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


(om/add-root! reconciler App (gdom/getElement "app"))

Вот файл project.clj:

(defproject om-tutorial "0.1.0-SNAPSHOT"
  :description "My first Om program!"
  :dependencies [[org.clojure/clojure "1.7.0"]
                 [org.clojure/clojurescript "1.7.170"]
                 [org.omcljs/om "1.0.0-alpha24"]
                 [figwheel-sidecar "0.5.0-SNAPSHOT" :scope "test"]])

2 ответа

У меня была та же проблема в моем приложении, и я нашел обходной путь (хотя это может быть не лучшим решением). Вы можете создать свои компоненты, передав свойства om родительского компонента.

Ваше приложение могло бы выглядеть так:

(defui App
  Object
  (render [this]
          (dom/div nil (topics-view (om/props this)))))

IQuery безусловно, лучшее решение, но у меня все та же проблема, что и у вас. Этот обходной путь работает в моих проектах на данный момент, и я обязательно посмотрю на IQuery снова.

редактировать

Учебник о компонентах, идентичности и нормализации объясняет, что вы должны сделать, чтобы обновить пользовательский интерфейс, когда это необходимо. Это приводит к более идиоматическому решению.

Om Next не хочет запускать повторное чтение запросов по соображениям производительности, чтобы избежать ненужного вызова для них функций чтения и избежать бесполезных повторных визуализаций. Чтобы указать, что компоненты, которые запрашивают :current-topic Если необходимо выполнить повторный рендеринг (и вызвать соответствующую функцию чтения), вы можете указать эти ключи в конце транзакционного вектора:

(om/transact! this
  `[(switch-topic ~{:name (.. e -target -value)})
    :current-topic])

Ссылка: https://github.com/omcljs/om/wiki/Documentation-(om.next)

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