Search code examples
clojurescriptreagent

re-frame: nvd3 graph doesn't respond to when its component's subscriptions are updated


I'm using the re-frame cljs framework which uses reagent as its view library. I have an nvd3 graph component that I want to be updated when its subscriptions update.

Unfortunately, the graph never updates itself after the initial call to :component-did-mount. :component-will-update is never called again after the intial render.

I want the graph to update itself as the subscription notifies the components of the dat it's listening to being changed.

Here's the graph-container component:

(defn weight-graph-container
  []
  (let [weight  (subscribe [:weight-change])
        bodyfat (subscribe [:bodyfat-change])
        weight-amount (reaction (get @weight :amount))
        weight-unit   (reaction (get @weight :unit))
        bf-percentage (reaction (get @bodyfat :percentage))
        lbm           (reaction (lib/lbm @weight-amount @bf-percentage))
        fat-mass      (reaction (- @weight-amount @lbm))]
    (reagent/create-class {:reagent-render weight-graph
                           :component-did-mount (draw-weight-graph @lbm @fat-mass "lb")
                           :display-name "weight-graph"
                           :component-did-update (draw-weight-graph @lbm @fat-mass "lb")})))

Here's the graph component:

(defn draw-weight-graph [lbm fat-mass unit]
  (.addGraph js/nv (fn []
                     (let [chart (.. js/nv -models pieChart
                                     (x #(.-label %))
                                     (y #(.-value %))
                                     (showLabels true))]
                       (let [weight-data [{:label "LBM" :value lbm} {:label "Fat Mass" :value fat-mass}]]
                         (.. js/d3 (select "#weight-graph svg")
                                   (datum (clj->js weight-data))
                                   (call chart)))))))

Finally, here's the component that the graph renders into:

(defn weight-graph []
  [:section#weight-graph
   [:svg]])

What am I missing? Thanks for any help.


Solution

  • The following code solves your problem:

    (defn draw-weight-graph
      [d]
      (let [[lbm fat-mass unit] (reagent/children d)]
        (.addGraph js/nv (fn []
                           (let [chart (.. js/nv -models pieChart
                                           (x #(.-label %))
                                           (y #(.-value %))
                                           (showLabels true))]
                             (let [weight-data [{:label "LBM" :value lbm} {:label "Fat Mass" :value fat-mass}]]
                               (.. js/d3 (select "#weight-graph svg")
                                   (datum (clj->js weight-data))
                                   (call chart))))))))
    
    (def graph-component (reagent/create-class {:reagent-render weight-graph
                                                :component-did-mount draw-weight-graph
                                                :display-name "weight-graph"
                                                :component-did-update draw-weight-graph}))
    
    (defn weight-graph-container
      []
      (let [weight  (subscribe [:weight-change])
            bodyfat (subscribe [:bodyfat-change])
            weight-amount (reaction (get @weight :amount))
            weight-unit   (reaction (get @weight :unit))
            bf-percentage (reaction (get @bodyfat :percentage))
            lbm           (reaction (lib/lbm @weight-amount @bf-percentage))
            fat-mass      (reaction (- @weight-amount @lbm))]
      (fn []
         [graph-component @lbm @fat-mass "lb"])))