d3 / React / Hooks - обновления не очищают старые директивы и группы - PullRequest
0 голосов
/ 30 апреля 2020

Я работаю над реализацией обновления гистограммы в реальном времени, используя d3 с React и Hooks.

Хотя новые группы 'g' и rect s добавляются в svg, старые группы, похоже, не очищаются. Таким образом, ректы просто добавляются поверх старых ректов, как и группы осей.

Я использую API .join (), поэтому мне не нужно выполнять очистку вручную с помощью exit.remove () правильно? Я совершенно новичок в d3, так что простите за неопределенность.

приложение:

import React, { useRef, useEffect, useState } from 'react';
import * as firebase from 'firebase';
import Chart from './Chart';

function useInterval(callback, delay) {
  const savedCallback = useRef();

  // Remember the latest callback.
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

function App() {
  const [menu, setMenu] = useState([]);

  const db = firebase.firestore();

  useEffect(() => {
    db.collection('dishes')
      .get()
      .then((res) => {
        let data = [];
        for (let doc of res.docs) {
          data.push(doc.data());
        }
        setMenu(data);
      });
  }, []);

  useInterval(() => {
    let newMenu = [];
    newMenu = [...menu];

    if (newMenu[0] && newMenu[0].hasOwnProperty('orders')) {

      newMenu[0].orders += 50;

      setMenu(newMenu);
    }
  }, 3000);

  return (
    <div className="App">{menu.length > 0 ? <Chart data={menu} /> : null}</div>
  );
}

export default App;

Компонент диаграммы:

import React, { useRef, useEffect } from 'react';
import * as d3 from 'd3';

const Chart = ({ data }) => {
  const height = 600;

  // Generate a ref instance
  const svgRef = useRef();
  useEffect(() => {
    const svg = d3.select(svgRef.current);

    const margin = { top: 20, right: 20, bottom: 100, left: 100 };
    const graphWidth = 600 - margin.left - margin.right;
    const graphHeight = height - margin.top - margin.bottom;

    // Find the maximum order value
    const max = d3.max(data, (d) => d.orders);

    // Establish the y scale
    // i.e., map my max value to the pixel max value ratio
    const y = d3
      .scaleLinear()
      .domain([0, max * 1.25])
      .range([graphHeight, 0]);

    // Calculates width and coordinates for each bar
    // Can add padding here
    const x = d3
      .scaleBand()
      .domain(data.map((item) => item.name))
      .range([0, graphHeight])
      .padding(0.25);

    const graph = svg
      .append('g')
      .attr('width', graphWidth)
      .attr('height', graphWidth)
      .attr('transform', `translate(${margin.left}, ${margin.top})`);

    //   Creat axis groups for legends and labels
    const xAxisGroup = graph
      .append('g')
      .attr('transform', `translate(0,${graphHeight})`);
    const yAxisGroup = graph.append('g');

    // Append the graph to the DOM
    graph
      .selectAll('rect')
      .data(data, (entry, i) => entry)
      .join(
        (enter) => enter.append('rect'),
        (update) => update.append('class', 'new'),
        (exit) => exit.remove()
      )
      .transition()
      .duration(300)
      .attr('width', x.bandwidth)
      .attr('height', (d) => graphHeight - y(d.orders))
      .attr('fill', 'orange')
      .attr('x', (d) => x(d.name))
      .attr('y', (d) => y(d.orders));

    //  Create the axes
    const xAxis = d3.axisBottom(x);
    const yAxis = d3
      .axisLeft(y)
      //   .ticks(3)
      .tickFormat((d) => d + ' orders');

    // Append the axes to the graph
    xAxisGroup.call(xAxis);
    yAxisGroup.call(yAxis);

    xAxisGroup
      .selectAll('text')
      .attr('transform', 'rotate(-40)')
      .attr('text-anchor', 'end');
  }, [data]);

  return (
    <div>
      <svg ref={svgRef} height={height} width="600" />
    </div>
  );
};

export default Chart;
...