This example is based on a blog post by Nicolas Hery. It utilizes ES6 classes and ReactJS's lifecycle methods to keep the D3 component updated
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello, d3React!</title>
<style>
.d3Component {
width: 720px;
height: 120px;
}
</style>
</head>
<script src="https://fb.me/react-15.2.1.min.js"></script>
<script src="https://fb.me/react-dom-15.2.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<body>
<div id="app" />
<script type="text/babel" src="d3_react.js"></script>
</body>
</html>
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
d3React: new d3React()
};
this.getd3ReactState = this.getd3ReactState.bind(this);
}
getd3ReactState() {
// Using props and state, calculate the d3React state
return ({
data: {
x: 0,
y: 0,
width: 42,
height: 17,
fill: 'red'
}
});
}
componentDidMount() {
var props = {
width: this._d3Div.clientWidth,
height: this._d3Div.clientHeight
};
var state = this.getd3ReactState();
this.state.d3React.create(this._d3Div, props, state);
}
componentDidUpdate(prevProps, prevState) {
var state = this.getd3ReactState();
this.state.d3React.update(this._d3Div, state);
}
componentWillUnmount() {
this.state.d3React.destroy(this._d3Div);
}
render() {
return (
<div>
<h1>{this.props.message}</h1>
<div className="d3Component" ref={(component) => { this._d3Div = component; } } />
</div>
);
}
}
class d3React {
constructor() {
this.create = this.create.bind(this);
this.update = this.update.bind(this);
this.destroy = this.destroy.bind(this);
this._drawComponent = this._drawComponent.bind(this);
}
create(element, props, state) {
console.log('d3React create');
var svg = d3.select(element).append('svg')
.attr('width', props.width)
.attr('height', props.height);
this.update(element, state);
}
update(element, state) {
console.log('d3React update');
this._drawComponent(element, state.data);
}
destroy(element) {
console.log('d3React destroy');
}
_drawComponent(element, data) {
// perform all drawing on the element here
var svg = d3.select(element).select('svg');
svg.append('rect')
.attr('x', data.x)
.attr('y', data.y)
.attr('width', data.width)
.attr('height', data.height)
.attr('fill', data.fill);
}
}
ReactDOM.render(<App message="Hello, D3.js and React!"/>, document.getElementById('app'));
Place the contents of d3_react.html
and d3_react.js
in the same directory and navigate a web browser to the d3React.html file. If all goes well, you will see a header stating Hello, D3.js and React!
rendered from the React component and a red rectangle below from the custom D3 component.
React uses refs to "reach out" to the component instance. The lifecycle methods of the d3React
class require this ref to append, modify, and remove DOM elements. The d3React
class can be extended to create more custom components and inserted anywhere a div.d3Component
is created by React.