You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Reduce回调错误修改数组:为何dataMonth状态被意外修改?

问题原因及解决方案

核心原因:浅拷贝导致引用共享

你用{...dataMonth}做的是浅拷贝——它只会复制对象的第一层属性,对于嵌套的子对象(比如dataMonth.datasets里的每个元素),拷贝的是它们的内存引用,而不是创建全新的独立对象。所以你修改dataYear.datasets里的value.data时,其实是在修改和dataMonth.datasets共享的同一个对象,自然会连带修改dataMonth。

代码问题拆解

  • dataYear = { ...dataMonth }:仅复制了dataMonth的顶层属性(如labels、datasets),但datasets数组里的每个元素还是和原dataMonth里的元素指向同一块内存。
  • 后续forEach中修改value.data时,value是原dataMonth.datasets元素的引用,操作会直接同步到dataMonth上。

解决办法:改用深拷贝

需要完全复制嵌套的所有对象,切断引用关联,常用两种方式:

方式1:用JSON序列化(适合无特殊类型的场景)

如果dataMonth里没有函数、Symbol、循环引用这类JSON无法序列化的内容,这种方式最简单:

dataYear = JSON.parse(JSON.stringify(dataMonth));

方式2:手动递归深拷贝(适合复杂场景)

如果有特殊类型,可自己写递归拷贝逻辑,比如:

function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (obj instanceof Array) return obj.map(item => deepClone(item));
  const clonedObj = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clonedObj[key] = deepClone(obj[key]);
    }
  }
  return clonedObj;
}

// 使用
dataYear = deepClone(dataMonth);

修正后的完整代码

另外注意原代码里reduce的写法有问题(没传初始值,且直接在回调里修改value.data),一起修正:

const [dataMonth, setDataMonth] = useState(null);
let dataYear = {};

useEffect(() => {
    if (dataMonth !== null) {
        // 深拷贝切断引用
        dataYear = JSON.parse(JSON.stringify(dataMonth));
        dataYear.labels = ["Année " + year];
        dataYear.datasets.forEach((value) => {
            // 正确求和,传入初始值0避免空数组报错
            const total = value.data.reduce((prev, val) => prev + val, 0);
            value.data = [total];
        });
    }
}, [dataMonth, year]); // year作为依赖要加入数组

内容的提问来源于stack exchange,提问作者Hello World

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 22:48:52