I\'ve been trying to make a draggable d3 force layout in React for a while now. React has to be able to interact with the nodes in the graph. For example, when you click on
You define force simulation twice in your code. First time - string 7 in your codepen and second time - string 113. Your dragStarted
and dragEnded
functions (which are defined globally) use force simulation from string 7, but it not specified (you did not pass nodes, links and other params to it).
You should move these function into the method when you define and specify your force simulation so componentDidMount
method for Graph
component should look like this (you should also rewrite your tick
handler function, and set force params only once (now you do it on each tick), check my fork of your pen):
componentDidMount() {
this.d3Graph = d3.select(ReactDOM.findDOMNode(this));
var force = d3.forceSimulation(this.props.data.nodes)
.force("charge", d3.forceManyBody().strength(-50))
.force("link", d3.forceLink(this.props.data.links).distance(90))
.force("center", d3.forceCenter().x(width / 2).y(height / 2))
.force("collide", d3.forceCollide([5]).iterations([5]))
function dragStarted(d) {
if (!d3.event.active) force.alphaTarget(0.3).restart()
d.fx = d.x
d.fy = d.y
}
function dragging(d) {
d.fx = d3.event.x
d.fy = d3.event.y
}
function dragEnded(d) {
if (!d3.event.active) force.alphaTarget(0)
d.fx = null
d.fy = null
}
const node = d3.selectAll('g.node')
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragging)
.on("end", dragEnded)
);
force.on('tick', () => {
this.d3Graph.call(updateGraph)
});
}