带有嵌套数组的React父属性会被传播复制的状态变量改变?

问题描述 投票:0回答:1

考虑以下因素:

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 数组的内存位置?

reactjs immutability react-props
1个回答
0
投票

让我们讨论同样的问题。

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' }], };
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.