网站搜索

reactjs - 如何在 React.StrictMode 中重新加热 d3 力模拟?


我已经决定在 React 中使用 D3 创建一个交互式力导向图,一切在仿真开始后都能正常工作,但在 React 严格模式下拖动功能不起作用。我认为这一定是由于 React StrictMode 18 中组件的挂载和重新挂载造成的,但我无法真正确定原因。

我的 useEffect 钩子的内容在哪里

useEffect(function () {
      if (!svgRef.current) return;
      const svg = d3.select(svgRef.current);

      const nodes = d3.map(
        props.nodes,
        (node): Node => ({
          ...node,
        })
      );
      const links = d3
        .map(props.links, (link) => ({
          source: nodes.find((node) => node.id === link.source),
          target: nodes.find((node) => node.id === link.target),
        }))
        .filter(
          (link): link is { source: Node; target: Node } =>
            link.source !== undefined && link.target !== undefined
        );

      const simulation = d3
        .forceSimulation(nodes)
        .force(
          "link",
          d3
            .forceLink(links)
            .id((_, i) => nodes[i].id)
            .distance(LINK_LENGHT)
        )
        .force("charge", d3.forceManyBody().strength(-NODE_REPULSION))
        .force("center", d3.forceCenter())
        .force("forceX", d3.forceX().strength(NODE_GRAVITY))
        .force("forceY", d3.forceY().strength(NODE_GRAVITY))
        .force("colide", d3.forceCollide(NODE_RADIUS * NODE_MARGIN))
        .on("tick", ticked);

      const link = svg
        .selectAll("line")
        .data(links)
        .enter()
        .append("line")
        .style("stroke", "#aaa");

      const node = svg
        .selectAll("circle")
        .data(nodes)
        .enter()
        .append("circle")
        .call(
          d3
            .drag<SVGCircleElement, Node>()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended)
        )
        .attr("fill", (node) => node.color))
        .attr("r", NODE_RADIUS)
        .attr("stroke", "#000000")
        .attr("stroke-width", 1);

      function ticked() {
        link
          .attr("x1", (d) => d.source.x!)
          .attr("y1", (d) => d.source.y!)
          .attr("x2", (d) => d.target.x!)
          .attr("y2", (d) => d.target.y!);

        node.attr("cx", (d) => d.x!).attr("cy", (d) => d.y!);
      }

      function dragstarted(event: any, d: Node) {
        if (!event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
      }

      function dragged(event: any, d: Node) {
        console.log(simulation.alpha());
        d.fx = event.x;
        d.fy = event.y;
      }

      function dragended(event: any, d: Node) {
        if (!event.active) simulation.alphaTarget(0.0001);
        d.fx = null;
        d.fy = null;
      }
    },
    [props.nodes.length, props.links.length]
)

任何线索或帮助都将不胜感激。

答案#1

由于在移除 StrictMode 后它可以正常工作,我推测我需要在我的 useEffect 中添加一个清理函数。结果发现我只需要像这样移除我插入的链接和节点。

return () => {
    node.remove();
    link.remove();
}