This does not seem to be happening as the Quick Start tutorial says:
In Om Next application state changes are managed by a reconciler. The reconciler accepts novelty, merges it into the application state, finds all affected components based on their declared queries, and schedules a re-render.
When I change the select box the mutate function updates the state, but the App component's render function never executes. I can see with @app-state in the REPL that the state has changed and I never see the output in the console from the prn in the App's render function. This is all I see in the console:
[1955.847s] [om.next] transacted '[(om-tutorial.core/switch-topic {:name "b"})], #uuid "c3ba6741-81ea-4cbb-8db1-e86eec26b540"
"read :default" :topics
If I update the state from the REPL with (swap! app-state update-in [:current-topic] (fn [] "b"))
then the App's render function does execute. Here is the console output:
"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"}]}
Here is the full code:
(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"))
Here is the project.clj file:
(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"]])
I had the same issue in my application and found a workaround (although this might not be the best solution). You can construct your components by passing the om properties of the parent component.
Your ui App could would then look like this:
(defui App
Object
(render [this]
(dom/div nil (topics-view (om/props this)))))
IQuery
is definitely the better solution, but I still have the same issue like you. This workaround works in my projects for now and I will definitely take a look at IQuery
again.
Edit
The tutorial about Components, Identity and Normalization explains what you have to do to update the UI when it is necessary. This results in a more idiomatic solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With