React useState自动更新问题求助:修改新状态却改动原状态
React状态意外更新问题分析与解决
问题根源
这不是useState钩子的问题,而是JavaScript引用类型的特性导致的:
- 对象、数组属于引用类型,当你执行
let newStatObj = statistikat[0];时,并没有复制一个新对象,只是把原对象的引用地址赋值给了newStatObj。 - 后续修改
newStatObj的嵌套属性时,本质是直接操作原对象的内存地址,所以statistikat里的对象也会被同步修改。
解决方案
要避免修改原状态,必须创建原对象的深拷贝,断开引用关联。以下是几种可行的深拷贝方式:
方式1:使用structuredClone(推荐,现代浏览器支持)
structuredClone是原生API,支持大多数类型的深拷贝,包括嵌套对象:
const [newStatsObj, setNewStatsObj] = useState(() => { let muaj; let cmimiPerDit; if (dtSot.getMonth() === 5) muaj = "qershor"; if (dtSot.getMonth() === 6) muaj = "korrig"; if (dtSot.getMonth() === 7) muaj = "gusht"; if (dataForSpecific.dhoma === "dhomTeke") cmimiPerDit = 100; if (dataForSpecific.dhoma === "dhomCift") cmimiPerDit = 120; if (dataForSpecific.dhoma === "dhomFamiljare") cmimiPerDit = 150; // 深拷贝原对象,断开与statistikat的引用关联 let newStatObj = structuredClone(statistikat[0]); // 修改拷贝后的对象,不会影响原statistikat newStatObj[`fitimetSezon${dtSot.getFullYear()}`][muaj][ dtSot.getDate() - 1 ] += Number(cmimiPerDit); return newStatObj; });
方式2:使用JSON.parse(JSON.stringify())
如果你的对象里没有函数、Symbol、日期对象等特殊类型,可以用这种方式:
let newStatObj = JSON.parse(JSON.stringify(statistikat[0]));
注意:这种方式会丢失函数、Symbol,日期对象会被转成字符串,不适合复杂场景。
方式3:手动递归深拷贝
如果需要兼容旧浏览器,或者处理特殊类型,可以自己实现递归拷贝函数:
function deepClone(obj) { if (obj === null || typeof obj !== 'object') return obj; if (obj instanceof Date) return new Date(obj.getTime()); if (obj instanceof Array) return obj.map(item => deepClone(item)); if (typeof obj === 'object') { const clonedObj = {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { clonedObj[key] = deepClone(obj[key]); } } return clonedObj; } } // 使用: let newStatObj = deepClone(statistikat[0]);
注意事项
- 浅拷贝(比如
{...statistikat[0]})只能拷贝对象的第一层属性,嵌套的对象还是引用类型,修改内层属性依然会影响原状态,所以必须用深拷贝。 - useState的初始化函数只会在组件首次渲染时执行一次,后续组件更新不会重新运行这个函数。
内容的提问来源于stack exchange,提问作者florian dollani
相关产品推荐
相关产品推荐

