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

useQuery获取的数据意外被修改,原因及解决方法?

问题原因与解决方案

原因分析

你遇到的问题核心是引用类型的内存共享:
数组和对象都是引用类型,当你在useEffect中执行setRows(filtredByMonthExpensesOrTemplate)时,并没有创建新的数据副本,rows和filtredByMonthExpensesOrTemplate指向的是同一个内存地址。当用户修改表格中rows的元素(比如修改某个支出项的金额),本质上是在修改这个共享内存里的对象,自然会同步影响到filtredByMonthExpensesOrTemplate。

解决方法

1. 赋值时创建数据副本,切断引用关联

在useEffect中设置rows时,对filtredByMonthExpensesOrTemplate做浅拷贝或深拷贝,确保rows拥有独立的内存空间:

  • 如果数据结构简单(无嵌套对象、函数、Date等特殊类型),可以用对象展开运算符实现浅拷贝:
useEffect(() => {
  // 对数组中的每个对象都创建新副本
  const independentRows = filtredByMonthExpensesOrTemplate.map(item => ({ ...item }));
  setRows(independentRows);
}, [props.monthIndexSelected, expenses]);
  • 如果数据存在嵌套对象,需要用深拷贝:
    可以手写递归深拷贝函数,或者用JSON.parse(JSON.stringify())(注意:该方法会丢失函数、Date等特殊类型的信息),如果项目中引入了lodash,也可以用_.cloneDeep():
// 示例:用JSON方法深拷贝
useEffect(() => {
  const deepCopiedRows = JSON.parse(JSON.stringify(filtredByMonthExpensesOrTemplate));
  setRows(deepCopiedRows);
}, [props.monthIndexSelected, expenses]);

2. 优化useGetMonthlyExpensesOrTemplate Hook的返回值

确保这个Hook每次返回的都是全新的数组和对象,而不是复用原数据的引用:

  • 筛选数据时,使用filter、map等返回新数组的方法,不要直接修改原expenses数组;
  • 生成空模板时,每次都创建新的对象实例,而不是复用同一个对象引用。

3. 表格修改时遵循不可变更新原则

当用户在表格中修改数据时,不要直接修改rows中的元素,而是创建新的对象替换原元素,保证状态更新的不可变性:

const handleUpdateRow = (rowIndex, updatedData) => {
  setRows(prevRows => {
    // 先拷贝原数组
    const newRows = [...prevRows];
    // 替换目标行,创建新对象
    newRows[rowIndex] = { ...newRows[rowIndex], ...updatedData };
    return newRows;
  });
};

内容的提问来源于stack exchange,提问作者Jonathan Ribak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 00:11:09