omcljs / om

ClojureScript interface to Facebook's React
6.66k stars 363 forks source link

Calling om/update-state! on children of parameterized list component change props to root props #830

Closed ghost closed 7 years ago

ghost commented 7 years ago

Calling om/update-state! on children of parameterized list component changes props to root props. Clicking the button on TodoItem changes the props received from the item component from {:todo/id 1, :todo/title got milk?} to {:todo-list/items [{:todo/id 1, :todo/title got milk?} {:todo/id 2, :todo/title bar}]}.

(enable-console-print!)

(defui TodoItem
  static om/IQuery
  (query [T]
    '[:todo/title :todo/id])
  Object
  (initLocalState [_]
    {:edit-mode? true})
  (render [this]
    (let [on-double-click #(om/update-state! this update :edit-mode? not)
          {:keys [todo/title todo/id] :as item} (om/props this)
          edit-mode? (om/get-state this :edit-mode?)]
      (println "item" item)
      (dom/div nil
               (dom/div nil title)
               (dom/div nil id)
               (dom/button #js {:onClick on-double-click})
               (if edit-mode?
                 (dom/b nil "double click me; ")
                 (dom/b nil "double click me;!! "))))))

(def todo-item (om/factory TodoItem {:keyfn :todo/id}))

(defui TodoList
  static om/IQueryParams
  (params [this]
    {:id {}})
  static om/IQuery
  (query [_]
    [{'(:todo-list/items ?id) `~(om/get-query TodoItem)}])
  Object
  (render [this]
    (let [{:keys [todo-list/items]} (om/props this)]
      (apply dom/div nil
             (for [item items]
               (todo-item item))))))

(def todo-list (om/factory TodoList))

(defmulti read om/dispatch)
(defmulti mutate om/dispatch)

(defmethod read :default
  [_ _ _]
  nil)

(defmethod read :todo-list/items
  [{:keys [state]} _ _]
  {:value (:todo-list/items @state)})

(def state (atom {:todo-list/items [{:todo/id 1 :todo/title "got milk?"}
                                    {:todo/id 2 :todo/title "bar"}]}))

(def parser (om/parser {:read read :mutate mutate}))

(defonce reconciler
         (om/reconciler {:state state
                         :parser parser
                         :remotes []}))

(om/add-root! reconciler
              TodoList (js/document.getElementById "app"))

Similarly, example code from Quick Start tutorial can be changed to behave just like above, by making item component define query.


(enable-console-print!)

(def app-state
  (atom
    {:app/title "Animals"
     :animals/list
                [{:animal/id 1 :animal/name "Ant"}
                 {:animal/id 2 :animal/name "Antelope"}
                 {:animal/id 3 :animal/name "Bird"}
                 {:animal/id 4 :animal/name "Lion"}]}))

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

(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 :animals/list
  [{:keys [state] :as env} key {:keys [start end]}]
  {:value (subvec (:animals/list @state) start end)})

(defui Animal
  static om/IQuery
  (query [this]
    '[:animal/id :animal/name])
  Object
  (initLocalState [this]
    {:mode true})
  (render [this]
    (let [{:keys [animal/id animal/name]} (om/props this)
          mode (om/get-state this :mode)]
      (dom/div nil
               (dom/div nil (str id ". " name))
               (dom/p nil (str mode))
               (dom/button #js {:onClick #(om/update-state! this update :mode not)})))))

(def animal (om/factory Animal))

(defui AnimalsList
  static om/IQueryParams
  (params [this]
    {:start 0 :end 3})
  static om/IQuery
  (query [this]
    [:app/title {'(:animals/list {:start ?start :end ?end}) (om/get-query Animal)}])
  Object
  (render [this]
    (let [{:keys [app/title animals/list]} (om/props this)]
      (dom/div nil
               (dom/h2 nil title)
               (apply dom/ul nil
                      (map animal list))))))

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

(om/add-root! reconciler
              AnimalsList (gdom/getElement "app"))
anmonteiro commented 7 years ago

your queries don't look valid. A parameterized join should be written as:

`({:animals/list ~(om/get-query Animal)} {:start ~'?start :end ~'?end})

Making that change makes your example work properly.