首页 > 解决方案 > 更新 d3 数据反应钩子

问题描述

得到了这段代码:

export default function Chart({ data, changeData}) {
  console.log(data);
  const ref = useRef();

  const createGraph = (data) => {
    var margin = { top: 20, right: 20, bottom: 30, left: 40 },
      width = 1360 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;

    var x = d3.scaleBand().range([0, width]).padding(0.1);
    var y = d3.scaleLinear().range([height, 0]);

    var svg = d3
      .select(ref.current)
      .append("svg")
      .attr("id", "chart")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    x.domain(
      data.map(function (d) {
        return d.name;
      })
    );
    y.domain([
      0,
      d3.max(data, function (d) {
        return d.number;
      }),
    ]);

    // append the rectangles for the bar chart

    const bars = svg.selectAll().data(data).enter().append("rect");

    bars
      .attr("class", "bar")
      .attr("x", function (d) {
        return x(d.name);
      })
      .attr("width", x.bandwidth())
      .attr("y", function (d) {
        return y(d.number);
      })
      .attr("fill", "pink")
      .attr("height", function (d) {
        return height - y(d.number);
      })
      .on("mouseenter", function (actual, i) {
        d3.select(this).attr("opacity", 0.5);
        d3.select(this)
          .transition()
          .duration(300)
          .attr("opacity", 0.6)
          .attr("x", (a) => x(a.name) - 5)
          .attr("width", x.bandwidth() + 10);
      })
      .on("mouseleave", function (actual, i) {
        d3.select(this).attr("opacity", 1);
        d3.select(this)
          .transition()
          .duration(300)
          .attr("opacity", 1)
          .attr("x", (a) => x(a.name))
          .attr("width", x.bandwidth());
      });

    bars
      .append("text")
      .attr("class", "value")
      .attr("x", (a) => x(a.name) + x.bandwidth() / 2)
      .attr("y", (a) => y(a.number) + 30)
      .attr("fill", "blue")
      .attr("text-anchor", "middle")
      .text((a) => `${a.number}%`);

    d3.selectAll("bars").append("text").attr("class", "divergence");
    // add the x Axis
    svg
      .append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

    // add the y Axis
    svg.append("g").call(d3.axisLeft(y));
  };

  useEffect(() => {
    createGraph(data);
  }, [data]);

  return (
    <React.Fragment>
      <Filter>
        <h2>By</h2>
        <span>Popularity</span>
        <span onClick={() => changeData()}>Following</span>
      </Filter>
      <div style={{ marginLeft: "100px" }} ref={ref}></div>
    </React.Fragment>
  );
}

如何在每次渲染时更新数据?

我尝试创建一个函数来删除 SVG 并添加一个新函数,但是在第一次渲染后位置被弄乱了,我也尝试在 useEffect 中使用 exit() 和 remove() 但没有结果。

我试图实现的结果是我有一个图表,数据被提取并传递到这个组件中,每当我按下按钮时,数据都会改变。我现在不看任何动画,我只想看看如何更改数据。

标签: javascriptd3.js

解决方案


这有点晚了,但万一有人在搜索时遇到这个问题。

useEffect(() => {
  *// your d3 code here*
  instead of using .enter().append('rect')
  use .join('rect')
}, [data]) *// runs the d3 code every time the data changes*

更多关于 .join() https://observablehq.com/@d3/selection-join

在 return 语句中指定 svg 和 g 标签。
代替 div 上的 ref,在 svg 标签上执行 useRef
而不是 .append(g),执行 .select('.x-axis') 等
这样您就不会在每次运行 useEffect 时都附加新 g

  <svg ref={d3Container}>
    <g className="chart">
      <g className="x-axis" />
      <g className="y-axis" />
    </g>
  </svg>

我在观看本系列的第 1 和第 3 个视频后了解了上述内容https://muratorium.com/using-react-hooks-with-d3


推荐阅读