我相信我已经遵循了新React道具上的一般更新模式。当收到新的道具时,D3会进行数据计算和渲染,以便React不必渲染每个勾号。React + D3 Force布局:新链接有未定义位置
D3适用于静态布局。但是,当我在shouldComponentUpdate(nextProps)功能接收新的节点和链路,节点缺乏以下属性:
- 索引 - 节点阵列中的节点的索引(从零开始)。
- x - 当前节点位置的x坐标。
- y - 当前节点位置的y坐标。
因此,所有新节点都有<g tranform=translate(undefined, undefined)/>
,并且聚集在左上角。
我更新道具的方式是将新对象推向节点数组和链接数组。我不明白为什么D3没有像在componentDidMount()中的初始设置那样分配d.x和d.y。我一直在努力解决这个问题好几天。希望有人能帮助我。
这里是ForceLayout.jsx:
//React for structure - D3 for data calculation - D3 for rendering
import React from 'react';
import * as d3 from 'd3';
export default class ForceLayout extends React.Component{
constructor(props){
super(props);
}
componentDidMount(){ //only find the ref graph after rendering
const nodes = this.props.nodes;
const links = this.props.links;
const width = this.props.width;
const height = this.props.height;
this.simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).distance(50))
.force("charge", d3.forceManyBody().strength(-120))
.force('center', d3.forceCenter(width/2, height/2));
this.graph = d3.select(this.refs.graph);
this.svg = d3.select('svg');
this.svg.call(d3.zoom().on(
"zoom",() => {
this.graph.attr("transform", d3.event.transform)
})
);
var node = this.graph.selectAll('.node')
.data(nodes)
.enter()
.append('g')
.attr("class", "node")
.call(enterNode);
var link = this.graph.selectAll('.link')
.data(links)
.enter()
.call(enterLink);
this.simulation.on('tick',() => {
this.graph.call(updateGraph);
});
}
shouldComponentUpdate(nextProps){
//only allow d3 to re-render if the nodes and links props are different
if(nextProps.nodes !== this.props.nodes || nextProps.links !== this.props.links){
console.log('should only appear when updating graph');
this.simulation.stop();
this.graph = d3.select(this.refs.graph);
var d3Nodes = this.graph.selectAll('.node')
.data(nextProps.nodes);
d3Nodes
.enter()
.append('g')
.attr("class", "node")
.call(enterNode);
d3Nodes.exit().remove(); //get nodes to be removed
// d3Nodes.call(updateNode);
var d3Links = this.graph.selectAll('.link')
.data(nextProps.links);
d3Links
.enter()
.call(enterLink);
d3Links.exit().remove();
// d3Links.call(updateLink);
const newNodes = nextProps.nodes.slice(); //originally Object.assign({}, nextProps.nodes)
const newLinks = nextProps.links.slice(); //originally Object.assign({}, nextProps.links)
this.simulation.nodes(newNodes);
this.simulation.force("link").links(newLinks);
this.simulation.alpha(1).restart();
this.simulation.on('tick',() => {
this.graph.call(updateGraph);
});
}
return false;
}
render(){
return(
<svg
width={this.props.width}
height={this.props.height}
style={this.props.style}>
<g ref='graph' />
</svg>
);
}
}
/** d3 functions to manipulate attributes **/
var enterNode = (selection) => {
selection.append('circle')
.attr('r', 10)
.style('fill', '#888888')
.style('stroke', '#fff')
.style('stroke-width', 1.5);
selection.append("text")
.attr("x", function(d){return 20}) //
.attr("dy", ".35em") // vertically centre text regardless of font size
.text(function(d) { return d.word });
};
var enterLink = (selection) => {
selection.append('line')
.attr("class", "link")
.style('stroke', '#999999')
.style('stroke-opacity', 0.6);
};
var updateNode = (selection) => {
selection.attr("transform", (d) => "translate(" + d.x + "," + d.y + ")");
};
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);
};
我试着推新节点插入到节点阵列在shouldComponentUpdate()函数,而不是在服务器修改阵列。但是新节点仍然出现在左上角未定义的位置。所以我猜我的问题是在shouldComponentUpdate()。任何帮助是极大的赞赏!!
编辑:发现Object.assign(...)不返回数组。改为将它改为array.slice()。现在所有的节点都是用一个位置渲染的,但根本没有链接。旧节点也从原始位置磨损。
Here is how it looks when new props go in and shouldComponentUpdate is triggered
我不明白为什么链接上的位置不对应的节点。
只要在shouldComponentUpdate中进行true/false检查并在componentWillUpdate中进行实际修改即可。这很难说,如果这能解决什么或你的问题是什么无需调试,但有错误的生命周期事件做错事可以肯定地制造搞笑的错误。你觉得你需要提供一个运行片段来获得更详细的帮助... –
我做了检查,它实际上在componentWillUpdate修改。我想我发现了一个导致它的问题,这是我在馈送对象而不是在simulation.nodes和simulation.links中的数组。最初,我使用了Object.assign(...),它返回一个对象;我已经将它改成nextProps.nodes.slice()来创建新的数组。所以现在所有的节点都有位置,但是它们在爆炸中被重新渲染并且根本没有链接。我会张贴图片和片段。 – heidipercyslinky
你是怎么解决这个问题的? – kurumkan