简体   繁体   English

React + d3力图不更新最后添加的节点的位置

[英]React + d3 force graph doesn't update position of last added node

I have a React component that lets me add nodes to a d3 force graph. 我有一个React组件,可以将节点添加到d3力图中。 Whenever I add a node its position in the underlying data is updated by the simulation, but its position is not updated visually until the next node is added. 每当我添加一个节点时,其在基础数据中的位置都会通过仿真进行更新,但是直到添加下一个节点后,它的位置才会被直观地更新。 Is anybody able to spot why? 有人能找出原因吗?

I've made a stripped back example that demonstrates the issue, which can also be seen in this codesandbox : 我做了一个简单的例子来演示这个问题,也可以在以下codeandbox中看到:

import React from "react";
import shortid from "shortid";
import * as d3 from "d3";

const WIDTH = 500;
const HEIGHT = 500;

class Graph extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      nodes: [],
      value: shortid(),
    };
  }

  handleChange = event => {
    this.setState({ value: event.target.value });
  };

  handleAddNode = event => {
    event.preventDefault();

    const newNodes = [...this.state.nodes, { id: this.state.value }];

    this.setState({ nodes: newNodes, value: shortid() });
  };

  componentDidMount() {
    this.initialise(this.state.nodes);
    this.draw(this.state.nodes);
  }

  componentDidUpdate() {
    this.draw(this.state.nodes);
  }

  initialise = nodes => {
    const container = d3
      .select(this.svg)
      .attr("width", WIDTH)
      .attr("height", HEIGHT)
      .append("g")
      .attr("class", "container")
      .attr("transform", "translate(" + WIDTH / 2 + "," + HEIGHT / 2 + ")");

    container.append("g").attr("class", "nodes");

    this.simulation = d3
      .forceSimulation(nodes)
      .alphaDecay(0.2)
      .force("charge", d3.forceManyBody())
      .force("collide", d3.forceCollide().strength(1))
      .on("tick", this.ticked);
  };

  draw = nodes => {
    this.nodesSelection = d3
      .select(".nodes")
      .selectAll(".node")
      .data(nodes);

    this.nodesSelection
      .enter()
      .append("circle")
      .attr("class", "node")
      .attr("data-id", d => d.id)
      .style("fill", "red")
      .attr("r", 5);

    this.simulation.nodes(nodes);
    this.simulation.alpha(1).restart();

    console.log(nodes);
  };

  ticked = () => {
    this.nodesSelection.attr("cx", d => d.x).attr("cy", d => d.y);
  };

  render() {
    return (
      <div className="c-graph-container">
        <form className="c-node-creator">
          <div>
            <input
              type="text"
              value={this.state.value}
              onChange={this.handleChange}
            />
          </div>
          <button onClick={this.handleAddNode}>Add</button>
        </form>
        <svg ref={svg => (this.svg = svg)} className=".c-graph" />
      </div>
    );
  }
}

export default Graph;

As correctly pointed out by Andrew in the comments the update selection (this.nodesSelection) doesn't include nodes that are in the enter selection (this.nodesSelection.enter()). 正如安德鲁(Andrew)在评论中正确指出的那样,更新选择(this.nodesSelection)不包括回车选择(this.nodesSelection.enter())中的节点。 Since selections are immutable, the update selection doesn't contain the entered nodes after entering unless you re-select or use a merge. 由于选择是不可变的,因此除非您重新选择或使用合并,否则更新选择在输入后不包含输入的节点。

So the fix in my case is to change the selection line to: 所以我的解决方法是将选择行更改为:

this.nodesSelection = this.nodesSelection
  .enter()
  .append("circle")
  .attr("class", "node")
  .attr("data-id", d => d.id)
  .style("fill", "red")
  .attr("r", 5)
  .merge(this.nodesSelection);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM