Почему компонент 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)