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
相关产品推荐
相关产品推荐

