我想我在 ReactJS 中遗漏了一些关于 useState() 钩子的相当基本的东西?

I think I'm missing something fairly basic about useState() hook in ReactJS?

我正在尝试使用 useState() 将相同的对象数组添加到两个不同的有状态变量,这样我就可以允许用户对第一个应用更改,然后放弃更改并使用第二个恢复到通过选择一个按钮的开始状态。我是 React 的新手,结果 没有 像预期的那样工作!当用户输入更改时,出于某种原因,这些更改同时应用于 both,因此现在没有任何内容保留开始状态,如果用户选择 'Discard Changes'按钮。

我已将代码简化为以下示例:

import {useState} from 'react';
  
const budgetData = 
      [ 
        {   index: 0, category: 'Housing',   item: 'Mortgage',  amount: 650.99 },
        {   index: 1, category: 'Housing',   item: 'Insurance', amount: 275.50 }, 
        {   index: 2, category: 'Utilities', item: 'Hydro',     amount:  70.00 }      
      ];

function UpdateBudget()  {
    const backup = budgetData;
    const [data, setData ] = useState(budgetData); 
    const [dataBackup    ] = useState(backup); 

    const handleChange = ( (e, row) => {
      let selectedData = [...data];
      const {name, value} = e.target;
      selectedData[row][name] = value;
      setData(selectedData);
    });

    const handleReset = ( (e) => {
    setData(dataBackup);
    });

    return  (
      <div>
        <table>
          <thead>
            <tr>
              <th>Category</th> <th>Item</th> <th>Amount</th>
            </tr>
          </thead>

          <tbody>
              { data.map( (row) =>  (
                  <tr key={row.index}>
                    <td> <input value={row.category} name="category" onChange={(e) => handleChange(e, row.index)}/> </td>
                    <td> <input value={row.item    } name="item"     onChange={(e) => handleChange(e, row.index)}/> </td>
                    <td> <input value={row.amount  } name="amount"   onChange={(e) => handleChange(e, row.index)}/> </td>
                  </tr>
                ))
              }
          </tbody>
        </table>
        <div> 
            <button onClick={ (e) => handleReset (e)}> Discard Changes </button>
        </div>
        <div style={{ marginTop: 20, fontSize: 10 }}> * data       {data.length}       * {JSON.stringify(data)}       </div> 
        <div style={{ marginTop: 20, fontSize: 10 }}> * dataBackup {dataBackup.length} * {JSON.stringify(dataBackup)} </div>
      </div>
    );
}

感谢有人指点我,指出我在这里缺少的东西!

我怀疑是因为克隆数组不会创建子元素的副本。 selectedData[row][name] 这仍然引用相同的值,这是一个 JS 怪癖。

const budgetData = [
  ["foo"],
  ["bar"],
  ["baz"],
]
const backup = budgetData

const clonedData = [...backup]

clonedData[0][0] = "abc"

console.log(backup) // backup data is mutated

看看 lodash cloneDeep,它也会复制子值 https://lodash.com/docs/4.17.15#cloneDeep

const budgetData = [
  ["foo"],
  ["bar"],
  ["baz"],
]
const backup = budgetData

const clonedData = _.cloneDeep(backup)

clonedData[0][0] = "abc"

console.log(backup) // backup data is not mutated
console.log(clonedData) // cloned data holds the changes
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

另外,我认为您不需要第二个状态,因为您没有改变它。你可以放弃这个 const [dataBackup ] = useState(backup) 并只使用 backup