为何React子组件更新state会修改父组件对象引用?求解决方案
问题原因
你遇到的问题核心是引用类型的浅拷贝导致的共享内存地址:
- 父组件的
obj.values是嵌套对象,属于引用类型,当你用useState(obj.values)初始化子组件状态时,inp和父组件的obj.values指向的是同一个内存地址,并不是独立的副本。 - 你在
setInp里做的{ ...prev }只是浅拷贝——它只会复制外层对象的键,但里面的per1、per2这些子对象仍然和原obj.values里的子对象共享引用。所以当你修改nD[k1][k2]时,本质上还是在修改原父组件对象的属性值。
解决思路
要彻底切断子组件状态和父组件对象的引用关联,需要做深拷贝,同时在更新状态时也要保证嵌套对象的独立性。
1. 初始化状态时做深拷贝
不要直接把父组件的obj.values传给useState,而是先创建一个完全独立的深拷贝:
const ChildComponent = ({obj}) => { // 方式1:用JSON序列化/反序列化(适合无函数、Symbol等特殊类型的对象) const [inp, setInp] = useState(JSON.parse(JSON.stringify(obj.values))); // 方式2:手动递归深拷贝(适合复杂类型场景) // const deepClone = (obj) => { // if (typeof obj !== 'object' || obj === null) return obj; // const clone = Array.isArray(obj) ? [] : {}; // for (const key in obj) { // clone[key] = deepClone(obj[key]); // } // return clone; // }; // const [inp, setInp] = useState(deepClone(obj.values)); // ...后续代码 }
2. 更新状态时避免修改原引用
在setInp的回调里,不仅要拷贝外层对象,还要拷贝你要修改的嵌套子对象,确保每一层都是独立的:
const onChange = useCallback(({target}) => { setInp((prev) => { // 1. 拷贝外层对象 const newInp = { ...prev }; // 2. 拷贝要修改的子对象(k1对应的对象,比如per1) newInp[k1] = { ...newInp[k1] }; // 3. 修改拷贝后的子对象属性 newInp[k1][k2] = target.value; return newInp; }) }, [k1, k2]); // 注意依赖数组要加上k1、k2,避免闭包问题
如果嵌套层级更深,手动拷贝会很繁琐,可以用immer库简化——它允许你用“修改”的写法生成不可变对象:
import { produce } from 'immer'; const onChange = useCallback(({target}) => { setInp(produce(prev => { prev[k1][k2] = target.value; })); }, [k1, k2]);
3. 验证obj.values与inp是否相等
因为是复杂嵌套对象,不能直接用===(它只会比较引用地址),需要做深比较:
- 自己实现递归比较函数:
const isDeepEqual = (a, b) => { if (a === b) return true; if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) return false; const keysA = Object.keys(a); const keysB = Object.keys(b); if (keysA.length !== keysB.length) return false; for (const key of keysA) { if (!keysB.includes(key) || !isDeepEqual(a[key], b[key])) return false; } return true; }; // 使用示例 const isEqual = isDeepEqual(obj.values, inp);
- 或者使用
lodash的isEqual方法(如果项目已引入该库):
import isEqual from 'lodash/isEqual'; const isEqual = isEqual(obj.values, inp);
内容的提问来源于stack exchange,提问作者GTemp
相关产品推荐
相关产品推荐

