更新嵌套的useState似乎会修改原始数据

时间:2020-09-30 11:48:24

标签: reactjs material-ui

因此,我在表旁边有一个文本字段输入的实现,在该表中,我试图在将数据提交给API之前更新暂存数据的状态。

在Dialogs父组件中,我定义了要在表中显示为原始状态的数据。

我当前遇到的问题是输入的数据,尽管我没有直接接触这些数据,但还是以某种方式更新了原始数据的状态。

下面是它在Codesandbox上的复制品,因此,当您打开链接并在编辑值字段中键入内容时,不应更新当前的库存字段,而我不知道为什么。

>

CodeSandBox

这是修改状态的回调:

const handleUpdateDip = (value, tank) => {
  const newData = stagedData;
  const foundIndex = newData.dips.findIndex((d) => d.tank === tank);

  if (foundIndex !== -1) {
    newData.dips[foundIndex].currentStockValue = Number(value);

    setStage({
      ...stagedData,
      dips: newData.dips
    });
  }
};

是的,这对我来说似乎很奇怪,我一直在敲着键盘,试图了解自昨晚以来发生了什么事,因此将不胜感激!

4 个答案:

答案 0 :(得分:1)

您正在突变当前对象。试试这个

setStage((stage) => {
  const foundIndex = stage.dips.findIndex((d) => d.tank === tank);
  return {
    ...stage,
    dips: stage.dips.map((d, index) => {
      if (foundIndex === index) {
        return { ...d, currentStockValue: Number(value) };
      }
      return d;
    })
  };
});

代替这个

const foundIndex = stagedData.dips.findIndex((d) => d.tank === tank);

if (foundIndex !== -1) {
  stagedData.dips[foundIndex].currentStockValue = Number(value);

  setStage({
    ...stagedData,
    dips: stagedData.dips
  });
}

Edit 64136950/updating-nested-usestate-seems-to-modify-the-original-data/64137147#64137147

答案 1 :(得分:0)

我不明白为什么代码指示这样做时不应该更新它! handleUpdateDip()中的这一行:

stagedData.dips[foundIndex].currentStockValue = Number(value);

  1. 您不应该直接改变状态。您应该先对其进行复制,然后根据需要进行更改,然后将状态设置为新值,例如:
  const handleUpdateDip = (value, tank) => {
    const foundIndex = stagedData.dips.findIndex((d) => d.tank === tank);
    if (foundIndex !== -1) {
      const newStagedData = { ...stagedData };
      newStagedData.dips[foundIndex].currentStockValue = Number(value);
      setStage(newStagedData);
    }
  };
  1. stagedData.dips[foundIndex].currentStockValue = Number(value);此行更新了“当前库存”列中使用的currentStockValue的值。

答案 2 :(得分:0)

似乎输入字段左侧的表格单元格仅使用handleUpdateDip中更改的相同状态

<TableCell align="right" padding="none">
           {row.currentStockValue}
</TableCell>

<TableCell align="right" padding="none">
      <InputTextField
          id="new-dip"
          type="number"
          inputProps={{
          min: 0,
          style: { textAlign: "right" }
          }}
          defaultValue={row.currentStockValue}
          onChange={(event) =>
          handleUpdateDip(event.target.value, row.tank)
           }
      />

都是currentStockValuehandleUpdateDips在此行中发生了更改

stagedData.dips[foundIndex].currentStockValue = Number(value);

答案 3 :(得分:0)

我想我知道你在想什么。您认为,一方面,您正在用handleUpdateDip(event.target.value, row.tank)更新setStage({...})中的状态,所以您只更改了状态stagedData

但是,“当前库存”的值映射到您的data变量而不是stagedData

最后,您的问题是:为什么只操作data时为什么stagedData会发生变化。

当然会在这里发生:const [stagedData, setStage] = useState(() => data); (顺便说一句,您无需在此处使用函数,const [stagedData, setStage] = useState(data);很好)。您在此处通过引用传递数据,当您的setState命中时,引用将被更新,您的data也将被更新。 (另外一个顺便说一句:不要简单地setState调用状态变量设置函数,这是React中的类组件使用的东西。像要设置的状态一样调用它们,例如setStagedData)。

现在,您可以消除此引用,因为无论如何您只需要初始值。您可以通过传递一个副本来做到这一点,例如:const [stagedData, setStagedData] = useState({...data});但这仍然行不通-我不确定为什么,因为我对useState的内部运作不了解,但是原因可能是因为它只是浅表副本,而不是深表副本(您可以阅读有关此here的更多信息)。

但是,如果我们进行深层复制并将其传递进去,它将起作用,并且您的原始data将保持不变。您可以通过基本上先进行字符串化然后再对其进行解析来进行深层复制(作为警告,它不会复制该对象具有的任何方法)。

const copy = JSON.parse(JSON.stringify(data));
const [stagedData, setStagedData] = useState(copy);

就像那样,您当前的库存将保持不变: enter image description here

我为您的CodeSandBox分叉了,所以您可以自己查看。