操作数组副本(通过扩展运算符创建)更改内存中的原始数组

Manipulating array copy (created via spread operator) changes original array in memory

https://codesandbox.io/s/chart-3yx8p

“图表”组件由 3 个图表层叠而成:

折线图只是呈现线条。第一个散点图在线上的每个点上呈现蓝色圆圈 - 我们将其称为“BlueScatter”。这两个接收相同的静态数据。第二个散点图呈现底部的黑点 - 我们将其称为“BlackScatter”。 BlackScatter 的数据处于需要更改的状态。

只要将鼠标悬停在其中一个蓝色圆圈上,就会发生两件事:

当您的鼠标离开圆圈时,工具提示消失,圆点将向下移动。然而后者并没有发生...

当您将鼠标悬停在一个圆圈上时,传递给该圆圈标签组件的“active”属性将设置为 true - 当您的鼠标离开时设置为 false。 “Tooltip”组件设置为 BlueScatter 上每个点的标签,因此 Tooltip 接收此 prop。 Tooltip 只是一个不可见的 SVG,用于锚定 Material UI 工具提示。 BlueScatter 的事件处理程序控制此逻辑(请参阅常量文件)。在 Tooltip 中,我使用从 Chart 传递的 props 来更改该索引处 BlackScatter 数据的坐标。

综上所述:
在 Chart 中,我将 INITIAL_HOVER_DATA 传播到状态数组中以设置 BlackScatter 的数据。在 Tooltip 中,我再次将该常量扩展到一个空数组中,以便在以下 if 语句中使用。过去我一直能够使用扩展运算符来制作数组的副本 - 但由于某种原因,工具提示中的第 14 行似乎正在操纵内存中的原始常量。这就是为什么黑点不 return 回到原来的位置。为什么会这样?取消注释第 16 行会使事情按预期工作,但理论上不需要它。

顺便说一下 - 如果有人可以提出更好的方法来实现相同的功能,我会喜欢反馈!

TLDR: 操作 INITIAL_HOVER_DATA 数组的副本会更改内存中的原始数组。

问题

即使您浅层复制数组,newHoverData[index].y = yValue / 2; 也是状态突变,因为您没有 复制每个更新的元素。每个元素仍然引用原始数组中的元素。

解决方案

浅拷贝数组要更新的元素

useEffect(() => {
  const { y: yValue } = data[index];
  setScatterHoverData(
    INITIAL_HOVER_DATA.map((el, i) =>
      i === index && active
        ? {
            ...el,
            y: yValue / 2
          }
        : el
    )
  );
}, [active]);