React has been a popular choice among developers. The number of applications on the web using React will rise sharply in the coming future. Almost 80% of the enterprise applications and data driven applications require some sort of data visualization. Using d3 for any data visualization is, in my opinion, the standard option on the web today.
But using a third party library like d3 with newer frameworks and libraries like React has been a question of concern for many. In this post we are going to look at the basic technique of intergrating d3 with React.
Using d3js with Reactjs, the d3 way
First, lets create a React component and name it GraphComponent
import React, { Component } from 'react' import * as d3 from 'd3' class GraphComponent from Component { constructor() { super(...arguments) } render() { return (<div classNames='graph-container' ref={node => this.node = node}/>) } } export default GraphCOmponent
Here we have simple component which defines a constructor()
and a render()
method. The render()
renders an empty div with class='graph-container'
. If you have been playing with d3js
then you know that to render a d3 graph we need a container element. The div.graph-container
serves that purpose.
To initialize d3
graph, we need to wait for the DOM
to mount and render the component. Lets intialize our graph. For this we add a method initD3Graph()
in the class.
initD3Graph() { if (!this.node) { return } const margin = { top: 20, right: 20, bottom: 20, left: 20 } const height = 400 - margin.top - margin.bottom const width = this.node.clientHeight - margin.left - margin.right const container = d3.select(this.node) const svg = container.append('svg') .attr('height', height + margin.top + margin.bottom) .attr('width', width + margin.left + margin.right) const g = svg.append('g') .attr('height', height) .attr('width', width) .attr('transform', `translate(${margin.left},${margin.top})`) const scale = { x: d3.scaleBand().rangeRound([0, width]).padding(0.1), y: d3.scaleLinear().rangeRound([height, 0]) } d3.csv('data.csv', (d) => { return d.value }, (error, data) => { if (error throw error x.domain(data.map(function(d) { return d.name; })) y.domain([0, d3.max(data, function(d) { return d.value; })]) g.append('g') .attr('class', 'axis axis--x') .attr('transform', `translate(0,${height})`) .call(d3.axisBottom(scale.x)) g.append('g') .attr('class', 'axis axis--y') .call(d3.axisLeft(scale.y).ticks(10, '%')) .append('text') .attr('transform', 'rotate(-90)') .attr('y', 6) .attr('dy', '0.71em') .attr('text-anchor', 'end') .text('Frequency') g.selectAll('.bar') .data(data) .enter().append('rect') .attr('class', 'bar') .attr('x', function(d) { return scale.x(d.name); }) .attr('y', function(d) { return scale.y(d.value); }) .attr('width', scale.x.bandwidth()) .attr('height', function(d) { return height - scale.y(d.value) }) }) }
While using d3
to generate graph, one thing to keep in mind while working with React
is that, d3
requires the DOM to be ready before manipulating the DOM to insert svg elements. Luckily React
provides a few life cycle methods to takecare of that.
componentDidMount() { this.initD3Graph() }
componentDidMount()
method is the least we require to initialize the graph. You can read more about React’s various lifecycle methods here.
Although, this method comes with a disadvantage that we are not utilizing React
‘s virtual DOM to update the view, rather we are depending on d3
‘s ability to manipulate DOM.