Компонент линейного графика D3 не обновляется внутри useEffect в React

#javascript #reactjs #d3.js #data-visualization #linechart

Вопрос:

Я получаю данные для своего линейного графика из API. Каждый раз, когда данные обновляются, они не перерисовываются с плавным переходом, а вместо этого рисуют новые линии в верхней части существующей диаграммы. Это выглядит грязно. У меня нет жизненного цикла, как я пою useEffect . Это как-то связано с line().remove().merge() этим ? Что я делаю не так?

CodeSandbbox

 useEffect(() => {
    const svg = d3
      .select(svgRef.current)
      .append("g")
      .attr("transform", "translate(0,"   -130   ")");
    const xScale = d3.scaleTime();
    const yScale = d3.scaleLinear();
    const xAxisDateFormat = d3.timeFormat("%b");
    
    // line generator
    const line = d3
      .line()
      .curve(d3.curveCatmullRom.alpha(1))
      .x((d) => xScale(new Date(d.date)))
      .y((d) => yScale(d.value));

    // draw line chart
    const lines = svg.append("g").attr("class", "lines");
    lines
      .selectAll(".line-group")
      .data(lineChartData.values)
      .enter()
      .append("g")
      .attr("class", "line-group")
      .append("path")
      .attr("class", "line")
      .attr("stroke", (d, i) => colorScale(d.lineColor))
      .attr("d", (d) => line(d.dataset));
  }, [lineChartData]);
 

Ответ №1:

Вы можете просто очистить содержимое svg перед добавлением новых элементов.

Вместо:

 const svg = d3
      .select(svgRef.current)
      .append("g")
 

делать:

  const svgEl = d3.select(svgRef.current);

 svgEl.selectAll('*').remove();

 const svg = svgEl
      .append('g')
 

Комментарии:

1. Это действительно возможно, но это приводит к ненужным вставкам/удалениям в DOM.

Ответ №2:

exit() отсутствует в вашем коде:

 const lines = svg.append("g")
  .classed("lines", true);
const groups = lines
  .selectAll(".line-group")
  .data(lineChartData.values);

const newGroups = groups.enter()
  .append("g")
  .classed("line-group", true);

newGroups.append("path")
  .classed("line", true)
  .attr("stroke", (d, i) => colorScale(d.lineColor))
  .attr("d", (d) => line(d.dataset));

const oldGroups = groups.exit();
oldGroups.remove();
 

Комментарии:

1. Спасибо, Майкл! Я не вижу, где в вашем примере используются новые группы?

2. Моя цель состояла в том, чтобы подчеркнуть, что enter() возвращает новые элементы ( newGroups ), в то время exit() как возвращает старые элементы ( oldGroups ), которые вам нужно удалить. Кроме того, важно отделить код, используемый для создания <g> элементов, от кода, который добавляет <path> элементы под ними (во избежание путаницы)… Это распространенная проблема, которую я вижу во многих примерах D3.

3. Спасибо, что рассказали об этом подробнее. К сожалению, это не работает.

4. В моем ответе есть ошибка: должно быть newGroups.append("path") вместо groups.append("path") (я исправил это в ответе). Помогает ли это?

5. Это то, что я изначально думал, так как newGroups на самом деле никогда не использовался, однако он по-прежнему рисуется поверх существующей диаграммы, поэтому я должен использовать решение, предоставленное @edgarkiljak.