考虑以下因素:
type ObjectWithNestedArray = {
array: { name: string }[];
};
const App = () => <Parent />;
const Parent = () => {
const objWithNestedArray = { array: [{ name: "A" }] };
return <Child objWithNestedArray={objWithNestedArray}></Child>;
};
const Child = (props: { objWithNestedArray: ObjectWithNestedArray }) => {
const [stateObjWithNestedArray, setStateObjWithNestedArray] = useState<ObjectWithNestedArray>();
const [count, setCount] = useState<number>(0);
const onChange = () => {
if (stateObjWithNestedArray) {
//const copyOfObj = structuredClone(stateObjWithNestedArray) as ObjectWithNestedArray;
const copyOfObj = { ...stateObjWithNestedArray };
copyOfObj.array[0].name = "B";
}
setCount(count + 1);
};
useEffect(() => {
if (props.objWithNestedArray) {
setStateObjWithNestedArray((current) => props.objWithNestedArray);
}
}, [props.objWithNestedArray]);
return (
<div>
<pre>obj={JSON.stringify(props.objWithNestedArray, null, 2)}</pre>
<button onClick={() => onChange()}>Click me</button>
</div>
);
};
当我渲染我的
App
时,我得到:
propsFormDef={
"array": [
{
"name": "A"
}
]
}
但是,当我单击按钮时,它会以某种方式修改传入的属性:
propsFormDef={
"array": [
{
"name": "B"
}
]
}
如果我将
onChange
中的行修改为注释掉的行:
const copyOfObj = structuredClone(stateObjWithNestedArray) as ObjectWithNestedArray;
那么 prop 值不会改变。
有人可以解释一下,当没有回调并且 props 在 React 中应该是不可变的时,修改 state 变量的扩展副本如何更改传入的属性?
是否因为传入的 prop 中的数组是通过引用传递的,类似地,扩展副本会复制引用,以便当我更改该数组中的值时,我实际上是在修改原始 prop 数组的内存位置?
让我们讨论同样的问题。
const obj = {
array: [{ name: 'A' }],
};
const copyOfObj = { ...obj }; // spread operator in use
观察结果:
a) 以下检查会产生错误值。这说明原来的对象和通过复制新创建的对象完全是两个不同的对象。
console.log(obj === copyOfObj); // false
b) 以下检查结果为真值。由此可见,虽然原来的对象和新创建的对象是完全不同的两个对象,但是这两个对象引用的数组对象是相同的。
console.log(obj.array === copyOfObj.array); // true
c) 因此,下面的赋值将会改变两个对象,这意味着它也会改变原始对象。请参阅下面的输出。
copyOfObj.array[0] = 'B';
console.log(obj); // { array: [{ name: 'B' }], };
console.log(copyOfObj); // { array: [{ name: 'B' }], };
解决方案:
可以使用函数 StructuredClone 来代替展开运算符。
请参阅下面的示例。
const obj = {
array: [{ name: 'A' }],
};
const copyOfObj = structuredClone(obj);
观察结果:
a) 以下检查会产生错误值。它表明两个对象是不同的。
console.log(obj === copyOfObj); // false
b) 以下检查会产生错误值。这两个对象引用的数组对象并不相同,是不同的。
console.log(obj.array === copyOfObj.array); // false
c) 因此,下面的赋值将会改变原始对象,而不是改变原始对象。
copyOfObj.array[0] = 'B';
console.log(obj); // { array: [{ name: 'A' }], };
console.log(copyOfObj); // { array: [{ name: 'B' }], };