Skip to content

Instantly share code, notes, and snippets.

@rgm
Created April 9, 2017 14:40
Show Gist options
  • Save rgm/7f2771a605956c17e5fb8c7409a7098a to your computer and use it in GitHub Desktop.
Save rgm/7f2771a605956c17e5fb8c7409a7098a to your computer and use it in GitHub Desktop.
hooking up D3 and React via om.next
(ns skeleton.fourth.charts
(:require
[om.next :as om :refer-macros [defui]]
[om.dom :as dom]
[sablono.core :as sab :refer-macros [html]]
[devcards.core :as dc :refer-macros [defcard]]
[cljsjs.d3 :as d3]))
(def canvas-size 200)
(def num-points 5)
(defn create-d3-chart
[props-data dom-node]
(let [radius 5
x-transform (fn [d i] (+ 10 (* i (/ canvas-size (+ num-points 1)))))
y-transform (fn [d i] (- canvas-size (* d 20) 10))]
(.. js/d3
(select dom-node)
(selectAll "circle")
(data (clj->js props-data))
(attr "r" radius)
(attr "cx" x-transform)
(attr "cy" y-transform)
enter
(append "circle")
(attr "r" radius)
(attr "cx" x-transform)
(attr "cy" y-transform))))
(defn create-d3-list
[props-data dom-node]
(.. js/d3
(select dom-node)
(selectAll "li")
(data (clj->js props-data))
(html identity)
enter
(append "li")
(html identity)))
(defn refresh-charts [c]
(let [data (:sample-data (om/props c))]
(create-d3-chart data (dom/node c "the-svg"))
(create-d3-list data (dom/node c "the-ul"))))
(defui ^:once D3IsInCharge
Object
(componentDidMount [this] (refresh-charts this))
(componentDidUpdate [this _ _] (refresh-charts this))
(render
[this]
(let [props (om/props this)]
(html [:div
[:h2 "d3-generated HTML"] [:ul#the-ul {:ref "the-ul"}]
[:h2 "d3-generated SVG"] [:svg#the-svg {:ref "the-svg"
:width canvas-size
:height canvas-size}]]))))
(def d3-in-charge (om/factory D3IsInCharge))
(defonce chart-state (atom {:sample-data (vec (range num-points))}))
(defn mutate-state! [_]
(swap! chart-state update-in [:sample-data] #(->> % (map inc) vec)))
(defcard "Tests of getting React and D3.js to play nice (from Ch. 9 of *D3.js In Action*).")
(defcard :d3-is-in-charge
"Test of letting D3 drive the DOM (aka. 'approach one' from the book). React
generates *only* the `<svg>` or `<ul>` container and we set a `ref` with
`[:svg {:ref \"the-ref\"} ,,,]`. We use that `ref` from `componentDidMount` and
`componentDidUpdate` using `(om.dom/node component \"the-ref\")` to get a
handle to the DOM node, and that forms the root D3 selection. Component's
`render` method only gets called once and we're relying on D3's
select/enter/exit."
(fn [props owner]
(html [:div
[:p [:label "Trigger a re-render: "
[:button {:on-click mutate-state!} "mutate-state!"]]]
(d3-in-charge @props)]))
chart-state
{:inspect-data true})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment