React + D3 exploration with the force layout:
Pro:
Con:
Use case: React app with a large and frequently updating visualization
The original using only D3: Enter-Update-Exit in Force Layout
The Force with React + D3, Approach #1
The Force with React + D3, Approach #2
The Force with React + D3, Approach #3
forked from sxywu's block: The Force with React + D3, Approach #3
xxxxxxxxxx
<meta charset="utf-8">
<head>
<script src="https://fb.me/react-0.14.3.js"></script>
<script src="https://fb.me/react-dom-0.14.3.js"></script>
<script src="https://npmcdn.com/babel-core@5.8.34/browser.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://underscorejs.org/underscore-min.js"></script>
<script src="generate_data.js"></script>
<link rel="stylesheet" href="example.css" type="text/css" />
</head>
<body>
<div id="main" />
<script type="text/babel">
var width = 960;
var height = 500;
var force = d3.layout.force()
.charge(-300)
.linkDistance(50)
.size([width, height]);
// *****************************************************
// ** d3 functions to manipulate attributes
// *****************************************************
var enterNode = (selection) => {
selection.classed('node', true);
selection.append('circle')
.attr("r", (d) => d.size)
.call(force.drag);
selection.append('text')
.attr("x", (d) => d.size + 5)
.attr("dy", ".35em")
.text((d) => d.key);
};
var updateNode = (selection) => {
selection.attr("transform", (d) => "translate(" + d.x + "," + d.y + ")");
};
var enterLink = (selection) => {
selection.classed('link', true)
.attr("stroke-width", (d) => d.size);
};
var updateLink = (selection) => {
selection.attr("x1", (d) => d.source.x)
.attr("y1", (d) => d.source.y)
.attr("x2", (d) => d.target.x)
.attr("y2", (d) => d.target.y);
};
var updateGraph = (selection) => {
selection.selectAll('.node')
.call(updateNode);
selection.selectAll('.link')
.call(updateLink);
};
// *****************************************************
// ** Graph and App components
// *****************************************************
var Graph = React.createClass({
componentDidMount() {
this.d3Graph = d3.select(ReactDOM.findDOMNode(this.refs.graph));
force.on('tick', () => {
// after force calculation starts, call updateGraph
// which uses d3 to manipulate the attributes,
// and React doesn't have to go through lifecycle on each tick
this.d3Graph.call(updateGraph);
});
},
shouldComponentUpdate(nextProps) {
this.d3Graph = d3.select(ReactDOM.findDOMNode(this.refs.graph));
var d3Nodes = this.d3Graph.selectAll('.node')
.data(nextProps.nodes, (node) => node.key);
d3Nodes.enter().append('g').call(enterNode);
d3Nodes.exit().remove();
d3Nodes.call(updateNode);
var d3Links = this.d3Graph.selectAll('.link')
.data(nextProps.links, (link) => link.key);
d3Links.enter().insert('line', '.node').call(enterLink);
d3Links.exit().remove();
d3Links.call(updateLink);
// we should actually clone the nodes and links
// since we're not supposed to directly mutate
// props passed in from parent, and d3's force function
// mutates the nodes and links array directly
// we're bypassing that here for sake of brevity in example
force.nodes(nextProps.nodes).links(nextProps.links);
force.start();
return false;
},
render() {
return (
<svg width={width} height={height}>
<g ref='graph' />
</svg>
);
}
});
var App = React.createClass({
getInitialState() {
return {
nodes: [],
links: [],
};
},
componentDidMount() {
this.updateData();
},
updateData() {
// randomData is loaded in from external file generate_data.js
// and returns an object with nodes and links
var newState = randomData(this.state.nodes, width, height);
this.setState(newState);
},
render() {
return (
<div>
<div className="update" onClick={this.updateData}>update</div>
<Graph nodes={this.state.nodes} links={this.state.links} />
</div>
);
},
});
ReactDOM.render(
<App />,
document.getElementById('main')
);
</script>
</body>
Modified http://d3js.org/d3.v3.min.js to a secure url
Modified http://underscorejs.org/underscore-min.js to a secure url
https://fb.me/react-0.14.3.js
https://fb.me/react-dom-0.14.3.js
https://npmcdn.com/babel-core@5.8.34/browser.min.js
https://d3js.org/d3.v3.min.js
https://underscorejs.org/underscore-min.js