Javascript D3总是合并节点,即使数据相同

Javascript D3总是合并节点,即使数据相同,javascript,reactjs,d3.js,dom,Javascript,Reactjs,D3.js,Dom,我创建了D3Treemap组件,它应该显示两个不同的文本,并将它们放在矩形中(不是html结构,而是页面上的位置) 由于这是正确的,我注意到当新数据出现在一个文本标记中时,其中一个文本标记会被更新。 因此,我认为预期的行为是,如果从.text函数返回的数据与DOM中已经存在的文本相同,则不要更新节点,如果不同,则更新节点。 实际发生的情况是,每次输入新数据时,总会有一个文本被替换 我只想在其内部的值不同时更新,而不是在任何时候更新 以下是我的代码: import * as d3Selection

我创建了D3Treemap组件,它应该显示两个不同的文本,并将它们放在矩形中(不是html结构,而是页面上的位置)

由于这是正确的,我注意到当新数据出现在一个文本标记中时,其中一个文本标记会被更新。 因此,我认为预期的行为是,如果从
.text
函数返回的数据与DOM中已经存在的文本相同,则不要
更新
节点,如果不同,则更新节点。 实际发生的情况是,每次输入新数据时,总会有一个文本被替换

我只想在其内部的值不同时更新
,而不是在任何时候更新

以下是我的代码:

import * as d3Selection from 'd3-selection';
import React, { useLayoutEffect, useRef } from 'react';
// ^^^^ imports stuff

export const Heatmap = ({
  width,
  height,
  padding = 1,
  data,
}) => {
  debugger;
  const ref = useRef();

  useLayoutEffect(() => {
    draw();
  }, [width, height, padding, data, ref.current]);

  const draw = () => {
    const svg = d3Selection.select(ref.current);
// util to get d3-treemap, nothing out of ordinary happening here apart from adding 
// opacity, value and color attributes to each node

    const root = makeTreemap({
      width,
      height,
      padding,
      data,
    });

    const rootLeaves = root.leaves();
    svg.attr('viewBox', `0 0 ${width} ${height}`).attr('preserveAspectRatio', 'xMinYMin meet');
    svg.attr('height', '100%').attr('width', '100%');

    const nodes = svg.selectAll('rect').data(root.leaves());

    nodes.join(
      (enter) =>
        enter
          .append('a')
          .attr('href', (d) => {
            if (d.link) {
              return d.link;
            }
            return '/';
          })
          .attr('target', '_blank')
          .append('rect')
          .attr('x', function (d) {
            return d.x0;
          })
          .attr('y', function (d) {
            return d.y0;
          })
          .attr('width', function (d) {
            return d.x1 - d.x0;
          })
          .attr('height', function (d) {
            return d.y1 - d.y0;
          })
          .style('stroke', 'black')
          .style('fill', function (d) {
            if (d.color) {
              return d.color;
            }
            return d.value >= 0 ? 'darkgreen' : 'darkred';
          })
          .style('opacity', function (d) {
            if (d.opacity) {
              return d.opacity;
            }
            return 1;
          }),
      (update) => update,
      (exit) => exit.remove()
    );

    svg
      .selectAll('text')
      .data(root.leaves())
      .join(
        (enter) =>
          enter
            .append('text')
            .attr('text-anchor', 'middle')
            .attr('x', function (d) {
              return (d.x0 + d.x1) / 2;
            })
            .attr('y', function (d) {
              return (d.y0 + d.y1) / 2 + 15;
            })
            // just code to get string to display
            .text(function (d) {
              const titleSplit = d.title.split(/(?=[A-Z][a-z])|\s+/g);
              const [_, ...otherParts] = titleSplit;
    enter code here
              return otherParts.join(' ')
            })
            .attr('fill-opacity', 0.7)
            .attr('fill', 'white'),
        (update) => update,
        (exit) => exit.remove()
      );

    svg
      .selectAll('vals')
      .data(root.leaves())
      .join(
        (enter) =>
          enter
            .append('text')
            .attr('x', function (d) {
              return (d.x0 + d.x1) / 2;
            })
            .attr('y', function (d) {
              return (d.y0 + d.y1) / 2;
            })
            .attr('text-anchor', 'middle')
            .text(function (d) {
              return d.title.split(/(?=[A-Z][a-z])|\s+/g)[0];
            })
            .attr('fill', 'white'),
        (update) => update,
        (exit) => exit.remove()
      );
  };

  return (
    <div style={{ width, height }}>
      <svg id={styles.svg} ref={ref} />
    </div>
  );
};
import*作为“d3选择”中的d3选择;
从“React”导入React,{useLayoutEffect,useRef};
//^^^^导入内容
导出常数热图=({
宽度,
高度,
填充=1,
数据,
}) => {
调试器;
const ref=useRef();
useLayoutEffect(()=>{
draw();
},[宽度、高度、填充、数据、当前参考];
常量绘图=()=>{
const svg=d3Selection.select(ref.current);
//util获取d3树映射,除了添加
//每个节点的不透明度、值和颜色属性
const root=makeTreemap({
宽度,
高度,
衬垫,
数据,
});
const rootLeaves=root.leaves();
attr('viewBox','0 0${width}${height}').attr('preserveAspectRatio','xMinYMin meet');
svg.attr('height','100%').attr('width','100%');
const nodes=svg.selectAll('rect').data(root.leaves());
nodes.join(
(输入)=>
进入
.append('a')
.attr('href',(d)=>{
如果(d.link){
返回d.link;
}
返回“/”;
})
.attr('target','u blank')
.append('rect')
.attr('x',函数(d){
返回d.x0;
})
.attr('y',函数(d){
返回d.y0;
})
.attr('width',函数(d){
返回d.x1-d.x0;
})
.attr(高度),功能(d){
返回d.y1-d.y0;
})
.style('笔划','黑色')
.样式(“填充”,功能(d){
如果(d.颜色){
返回d.color;
}
返回d.value>=0?'darkgreen':'darkred';
})
.style('opacity',function(d){
如果(d.不透明度){
返回d.opacity;
}
返回1;
}),
(更新)=>更新,
(exit)=>exit.remove()
);
svg
.selectAll('text')
.data(root.leaves())
.加入(
(输入)=>
进入
.append('文本')
.attr('text-anchor','middle')
.attr('x',函数(d){
返回(d.x0+d.x1)/2;
})
.attr('y',函数(d){
收益率(d.y0+d.y1)/2+15;
})
//只需编写代码即可显示字符串
.文本(功能(d){
常量titleSplit=d.title.split(/(?=[A-Z][A-Z])|\s+/g);
常数[,…其他部分]=标题片段;
在这里输入代码
返回其他部件。联接(“”)
})
.attr('fill-opacity',0.7)
.attr(“填充”、“白色”),
(更新)=>更新,
(exit)=>exit.remove()
);
svg
.selectAll('vals')
.data(root.leaves())
.加入(
(输入)=>
进入
.append('文本')
.attr('x',函数(d){
返回(d.x0+d.x1)/2;
})
.attr('y',函数(d){
收益率(d.y0+d.y1)/2;
})
.attr('text-anchor','middle')
.文本(功能(d){
返回d.title.split(/(?=[A-Z][A-Z])|\s+/g)[0];
})
.attr(“填充”、“白色”),
(更新)=>更新,
(exit)=>exit.remove()
);
};
返回(
);
};
我注意到,如果我删除第一个文本呈现部分,节点将被添加到DOM中,而不是在新数据到达时合并

我认为这是两件事之一

  • 我对节点何时获得更新的理解是错误的,即使在d3
    join
    中渲染的值完全相同,在进行DOM更新之前也没有缓存值或类似的内容,这意味着我自己必须检查值是否相等。(不知怎的)
  • 我在某一点上搞砸了,d3确实更新了部件,因为我告诉它要随时更新,即使数据是相同的

  • 不管是哪种方式,如果你们中的任何人都清楚,我的互联网朋友,为什么在这个特定的示例中,节点总是得到更新,即使从
    .text返回值也是如此(d=>…
    函数是相同的,如何真正避免发生更新我将非常感激。

    节点总是得到更新,因为您没有指定键函数,这是
    数据()的第二个参数
    。由于我不知道您的数据是什么样子,下面是一个简化的示例:假设您的数据如下:

    data = [{text: "hi", value: 1}, {text: "world", value: 2}]
    
    那么您的代码将如下所示:

    svg
      .selectAll('text')
      .data(data, d => d.text) // key function
      .join(
        (enter) =>
          enter
            .append('text')
            .text(d => d.text),
        (update) => 
          update
            .text(d.text),
        (exit) => exit.remove()
      );
    
    data = [{text: "hello", value: 1}, {text: "world", value: 3}]
    
    在这里,第二个参数是
    d=>d.text
    函数,它指定了如何对数据进行差异化。您可以将任何返回值的任意逻辑替换为该函数。在差异化时,D3将使用该函数来确定输入、更新和退出选项中的内容

    如果我们的新数据如下所示:

    svg
      .selectAll('text')
      .data(data, d => d.text) // key function
      .join(
        (enter) =>
          enter
            .append('text')
            .text(d => d.text),
        (update) => 
          update
            .text(d.text),
        (exit) => exit.remove()
      );
    
    data = [{text: "hello", value: 1}, {text: "world", value: 3}]
    
    在这里,D3使用键函数,在本例中是
    d=>d.text
    。根据该函数,第二个元素是相同的,因此它不会进入任何选择。但是第一个元素已经更改,因此它进入了您在
    join()
    中指定的
    update
    选择中

    见和g