React中useState嵌套对象值更新异常:一次操作数值累加两次
React useState更新嵌套状态时点击一次累加两次的解决方法
我在React中定义了嵌套结构的useState状态,实现increase函数更新指定item下option的amount时,出现点击一次但amount累加两次的异常:
原状态定义:
const [data, setData] = useState([ { id: 1, options: [{ id: 1, amount: 0 }, { id: 2, amount: 0 }] }, { id: 2, options: [{ id: 1, amount: 0 }, { id: 2, amount: 0 }] } ]);
尝试的increase函数:
const increase = (itemId, optionId) => { const copy = [...data]; const dataItem = copy.find((item) => item.id === itemId); const option = dataItem.options.find((item) => item.id === optionId); option.amount = option.amount + 1; setData(copy); }
问题原因
- 浅拷贝导致原状态被修改:
[...data]仅复制了数组外层,数组内的item对象、item里的options数组都是原状态的引用,修改option.amount时直接修改了原状态,违反React状态不可变原则。 - 严格模式的副作用检测:如果组件启用了React严格模式(Strict Mode),React会刻意执行两次状态更新来检测副作用,这就会导致你的修改被触发两次,出现点击一次累加两次的情况。
- 引用修改导致状态检测失效:直接修改引用类型状态会让React无法准确识别状态变化,引发状态不同步或不必要的重渲染。
解决方法
推荐实现:遵循不可变性+函数式更新
使用setData的函数式更新获取最新状态,同时逐层创建新对象/数组,避免修改原引用:
const increase = (itemId, optionId) => { setData(prevData => { return prevData.map(item => { // 不需要修改的item直接返回原对象 if (item.id !== itemId) return item; // 拷贝当前item,并更新options数组 return { ...item, options: item.options.map(option => { if (option.id !== optionId) return option; // 拷贝目标option,更新amount return { ...option, amount: option.amount + 1 }; }) }; }); }); };
备选方式:深拷贝(适合简单数据结构)
如果你的数据结构没有函数、Symbol等特殊类型,也可以用深拷贝来创建完全独立的状态副本:
const increase = (itemId, optionId) => { setData(prevData => { // 深拷贝创建新的状态副本 const newData = JSON.parse(JSON.stringify(prevData)); const targetItem = newData.find(item => item.id === itemId); const targetOption = targetItem.options.find(opt => opt.id === optionId); targetOption.amount += 1; return newData; }); };
关键要点
- 始终遵循React状态不可变原则:不要直接修改状态对象,而是创建新的对象/数组来更新状态。
- 使用函数式更新(
setState(prevState => {...}))确保每次获取的是最新的状态,避免异步更新导致的状态不一致。
内容的提问来源于stack exchange,提问作者nick
相关产品推荐
相关产品推荐

