d3-react-component
v2.0.1
Published
A class-based React Componenet for D3 visualizations
Downloads
13
Maintainers
Readme
D3-React-Component
D3-React-Component provides a React component wrapper for any d3 visualization. The component is called "D3Chart" and the entire package is only 1.9kb gzipped!(Seriously, this README is bigger than the entire build at ~8kb)
Design Philosophies
- [x] Should provide a single class component which can be extended to work with any existing d3 code
- [x] Should have a dead-simple API that contains only 3 methods: Create, Update, Destroy
- [x] Should work with d3 loaded in a script tag, just like if you weren't using react. It must not require any other d3-related react libraries to work
- [x] Zero dependency
Installation
NPM
npm install d3-react-component
Manual
- Copy
D3Chart.js
from the/src
folder of this repository into the/src
folder of your react app. - profit
Usage
- Load d3 in a script tag. (add
<script src="https://d3js.org/d3.v6.min.js"></script>
to your index.html file) - Create a class for your chart which extends the included D3Chart class component
- Write your chart initialization d3 code in your charts "create" method
- Write your chart update d3 code in your charts "update" method
- If you want to run custom d3 code before the component is unmounted from the dom, write this code in your charts "destroy" method.
- If you do not want to run custom d3 code before the component is unmounted from the dom, do not create a "destroy" method in your chart subclass. The parent class (
D3Chart
) will automagically take care of properly destroying the root svg element before the component is unmounted.
Notes:
- Class must have a create method
- Use this method to store chart-specific functions, variables, etc
- Class must have an update method
- Use this method to render the chart, update scale domains, perform enter/exit/update etc
- Class may have an optional destroy method
- Use this method to run custom d3 code before the component is unmounted from the DOM
- If no destroy method is present in your subclass, the parent class will take care of destroying the root svg element for you
Reasons to extend the provided Chart class (It is highly recommended that you do so)
- Margin math is done for you and is accessible from your subclass with `this.margin`
- Default margins are setup for you (20px default)
- A reference to the window.d3 object is mapped to `this.d3` for your subclass
Examples
Step1: Create a subclass for your chart (extend the provided D3Chart
class):
The first step is to create a class for your chart/visualization. The class that you create is required to have 2 methods, with an optional third method. Each of these methods directly maps to a phase in Reacts DOM lifecycle.
- Create: This method is called after the canvas node has been mounted to the DOM
- Use it to create the
SVG
element on the page, create your scales, and init any other d3 functions
- Use it to create the
- Update: This method is called any time a new
data
prop is passed to your charts subclass- Use it to render your chart, perform enter/update/exit transitions, update your scale domains, etc
- Destroy (optional method): This method is called before React unmounts the canvas node from the DOM
- Use it if you need to clean up after yourself.
Here is a very basic example of what it looks like when you extend the provided D3Chart
class. We'll call our chart "MyAwesomeChart" and we'll save it in its own file
"MyAwesomeChart.js"
import {D3Chart} from "d3-react-component" // If you used NPM
//import D3Chart from "./D3Chart.js" // If you performed a manual install
export default class MyAwesomeChart extends D3Chart {
/**
* Create and store the necessary functions for your vis
* @param canvasNode - The DOM node that will contain your vis. Append your svg element to this!
* @param data - The data that was initially passed to the vis
* @param props - any properties that you passed to your subclass. (Except margins, we'll discuss that later...)
*/
create( canvasNode, data, props ){
// Create the SVG element
this.svg = this.d3.select( canvasNode ) // Access the d3 object with this.d3
.append( "svg" )
.attr( "width", this.totalWidth ) // this.totalWidth provided by D3Chart parent class
.attr( "height", this.totalHeight ) // this.totalHeight provided by D3Chart parent class
.append( "g" )
.attr(
"transform",
`translate( ${this.margin.left},${this.margin.top} )` // This.margin object provided by Chart.js
);
// This is where you create and store your scales, axis formats, path generators, etc
}
/**
* Anytime your subclass component is updated with new data, this
* function is called. use it to update/transition your vis
* @param data - The new data
*/
update( data ){
// Code to update the chart
// This is where you update your scale domains, perform enter/exit/update etc
}
}
- Notice that we don't need any margin logic. Our parent class takes care of that for us by following the conventions laid out here
- Notice that you automagically get a reference to the d3 object. Use
this.d3
. ( again, the parent class takes care of this for us ) - If you chose not to extend the provided
Chart
class, you will have to get a reference to the d3 object yourself (typically justwindow.d3
). You will also have to perform any margin mathematics yourself. see here
Step 2: Render your chart component
Next, you need to simply render your chart as you would any other react component
"App.js"
import MyAwesomeChart from "./MyAwesomeChart.js"
const data = // Retrive your data however you want. Whether that be a static svg, database query, etc
function App(){
return(
<MyAwesomeChart
id={"some_unique_id"}
data={data}
width={1000}
height={500}
leftMargin={50}
rightMargin={20}
topMargin={35}
bottomMargin={20}
/>
);
}
- Note: All margins default to 0px, but we have overridden them here. These override will take effect even though your subclass does not implement any margin logic. This is take care of automatically by the D3Chart parent class
- Note: Both width and height default to 500px. Again, the D3Chart parent class allows this to happen without you having to write any logic for it in your subclass.
Custom properties
any custom properties that you pass to your subclass component will be passed to the create
method as the third parameter ( props )
App.js
import MyAwesomeChart from "./MyAwesomeChart.js"
function App(){
return(
<MyAwesomeChart
id={"some_unique_id"}
...
myCustomProperty={{
foo: "bar"
}}
/>
);
}
MyAwesomeChart.js
import {Chart} from "d3-react-component"
export default class MyAwesomeChart extends Chart {
create( canvasNode, data, props ){
console.log( props )
// Outputs { myCustomProperty : { foo: "bar" } }
...
}
update( data ){...}
}
Accessing Margins, Width, and Height
Margins
You can access the margins in your chart class with this.margin
. For instance, if you want to get the left margin, just use this.margin.left
.
Width and Height ( minus margins )
You can access the computed width and height with this.wdith
and this.height
respectively. ( these values are minus margins )
Total Width and Total Height ( width and height including margins )
If you would like to access the total width or total height you can use this.totalWidth
and this.totalHeight
respectively. ( these values include the margins )
Refactoring An existing chart
If you have existing D3 charts, they can be quite easily refactored into a subclass of D3Chart. I have adapted this chart to a Chart class so that you can compare them. (You will find it along with its data file in the "examples" folder of the repository)
To run the example:
- Start by placing data.csv into the /public folder
- Next, Place D3Chart.js and LineChart.js in the /src folder
- Edit App.js as to look as follows
import React from 'react';
import './App.css';
import {D3Chart} from "d3-react-component"
import LineChart from "./LineChart.js"
function App() {
return (
<LineChart
id={"LineChart"}
data={"data.csv"}
leftMargin={50}
width={1000}
height={500}
/>
);
}
export default App;