omcljs / om

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

set-query of a component nested under a unique ident join not reflected in get-query of any parent or ancestral components #860

Open bnoguchi opened 7 years ago

bnoguchi commented 7 years ago

Consider the following example:

(defui Child
  static om/IQueryParams
  (params [this]
    {:size 3})
  static om/IQuery
  (query [this]
    '[(:items {:size ?size})])
  Object
  (render [this]
    (let [{:keys [items]} (om/props this)]
      (dom/div nil
               (dom/div nil (str (count items)))
               (dom/button #js {:onClick #(om/set-query!
                                            this {:params {:size 20}})}
                           "Set query params to: {:size 20}")))))

(def child (om/factory Child))

(defui Parent
  static om/IQuery
  (query [this]
    ; Notice the ident [:child1 '_] here
    [:count {[:child1 '_] (om/get-query Child)}])
  Object
  (render [this]
    (let [props (om/props this)]
      (dom/div nil
               (dom/p nil (str "Root query: "
                               (om/get-query this)))
               (child (:child1 props))))))

(def reconciler
  (om/reconciler {:state  (atom {:count 1
                                 :child1 {:items [1 2 3]}})
                  :parser (om/parser
                            {:read   (fn [{:keys [state query] :as env} key _]
                                       (let [st @state]
                                         {:value (get st key)}))
                             :mutate (fn [{:keys [state]} _ _]
                                       {:value  {}
                                        :action #(swap! state update
                                                        :count inc)})})}))
(om/add-root! reconciler Parent dom-node)

If you update the nested component's (i.e., the Child instance's) query params, and then try to get the query of the parent component, the parent's query does not reflect the new child query params.

i.e.,

(om/get-query parent)
; => [:count {[:child1 _] [(:items {:size 3})]}]

I tracked the reason why this fails:

  1. The rendered path used in re-indexing won't reduce a unique ident to its corresponding dispatch key. See here
  2. The indexer uses the rendered path as a lookup key in :data-path->components. See here
  3. Meanwhile, :data-path->components index is updated using keys that are data-paths where the path reduces keys that are a ident to dispatch key. See here, here, here, and here.

The solution is to use dispatch keys instead of unique idents when calculating the rendered path during re-indexing.

Will submit a PR.