React如何创建独立备份状态,实现可编辑表格修改后可重置回初始数据
问题根源
你遇到的变量同步修改问题是浅拷贝导致的对象引用共享:
- 扩展运算符
[...数组]仅能复制数组本身的结构,数组内部存储的所有对象依然指向原始budgetData中的对象内存地址 - 你修改
selectedData[row][name]时,本质是修改了所有变量共享的同一个对象,因此dataBackup、原始budgetData都会同步变化 - 额外的问题:
dataBackup定义在组件函数体内,每次组件重渲染都会重新生成,且你直接将budgetData传给useState作为初始值,也会导致原始静态数据被意外修改
修复方案
我们不需要额外维护dataBackup变量,直接基于原始静态数据做深拷贝即可,修改两处核心逻辑:
- 初始化state和重置时,都对原始数据做全量拷贝,每个行对象单独复制
- 处理输入变更时,先复制要修改的行对象再更新属性,避免修改原引用
修正后完整代码
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 } ]; // 工具函数:拷贝预算数据,避免引用共享 const copyBudgetData = () => budgetData.map(item => ({...item})); function UpdateBudget() { const [dataWorking, setDataWorking ] = useState(copyBudgetData()); const handleChange = (e, row) => { // 先拷贝整个数组 let selectedData = [...dataWorking]; const {name, value} = e.target; // 再拷贝要修改的行对象,避免修改原引用 selectedData[row] = {...selectedData[row], [name]: value}; setDataWorking(selectedData); }; const handleReset = () => { // 重置时重新拷贝原始数据即可 setDataWorking(copyBudgetData()); }; return ( <div> <table> <thead> <tr> <th>Category</th> <th>Item</th> <th>Amount</th> </tr> </thead> <tbody> { dataWorking.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={handleReset}> Discard Changes </button> </div> <div style={{ marginTop: 20, fontSize: 10 }}> * budgetData {budgetData.length} * {JSON.stringify(budgetData)} </div> <div style={{ marginTop: 20, fontSize: 10 }}> * dataWorking {dataWorking.length} * {JSON.stringify(dataWorking)} </div> </div> ); } export default UpdateBudget;
优化说明
如果你的初始数据是从接口动态获取的而非静态常量,可以用useRef存储备份数据:
// 初始数据加载完成后存储备份 const dataBackup = useRef(copyBudgetData(originData)); // 重置时使用 setDataWorking(copyBudgetData(dataBackup.current));
内容的提问来源于stack exchange,提问作者Keith Arnold
相关产品推荐
相关产品推荐

