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

时间:2018-12-18 17:24:40

标签: javascript reactjs d3.js

我有一个React组件,可以将节点添加到d3力图中。每当我添加一个节点时,其在基础数据中的位置都会通过仿真进行更新,但是直到添加下一个节点后,它的位置才会被直观地更新。有人能找出原因吗?

我制作了一个简单的示例来演示该问题,in this codesandbox也可以看到该示例:

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;

1 个答案:

答案 0 :(得分:0)

正如安德鲁(Andrew)在评论中正确指出的那样,更新选择(this.nodesSelection)不包括回车选择(this.nodesSelection.enter())中的节点。由于选择是不可变的,因此除非重新选择或使用合并,否则更新选择在输入后不包含输入的节点。

所以我的解决方法是将选择行更改为:

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);