You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何React中forEach结合useState第二次执行时修改了数组首个对象?

React严格模式下useState更新数组对象异常问题解析

问题场景

基于useState实现简易购物车逻辑,想要更新数组中name为surya的对象。在React严格模式下代码会执行两次,第一次输出结果正确,但第二次运行时数组索引0、原name为aditya的对象被修改成了surya。

示例代码

const [coba, setCoba] = useState([
    { name: "aditya" },
    { name: "joni" },
    { name: "surya", money: 2000 },
  ]);

  coba.forEach((item) => {
    let arr = [];
    if (item.name === "surya") { // 注:原代码中写为item.nama,属于拼写错误
      item.money = 500000;
      arr.push(item);
      setCoba((prev) => {
        return [
          {
            ...prev,
            ...arr,
          },
        ];
      });
    }
  });
  console.log(coba);

控制台输出

第一次执行结果:

[
  { name: 'aditya' },
  { name: 'joni' },
  { name: 'surya', money: 500000 }
]

第二次执行结果:

[
  {
    '0': { name: 'surya', money: 500000 },
    '1': { name: 'joni' },
    '2': { name: 'surya', money: 500000 } 
  }
]

问题原因

  1. 直接修改原状态对象:循环中直接执行item.money = 500000,属于对React状态的直接突变。React状态设计为不可变值,直接修改会污染原状态,在严格模式的二次执行中,被污染的状态会被复用,导致异常结果。

  2. 错误的状态更新逻辑:setCoba回调里返回的[{...prev, ...arr}]完全破坏了数组结构。原数组prev是带数字索引的类对象,arr是包含修改后对象的数组,两者展开合并成一个新对象后,再被包裹进数组,最终得到的不是正常数组,而是包含索引键的对象数组。严格模式二次执行时,状态已经变成这个异常结构,循环处理时自然会出现索引0被覆盖的问题。

正确实现方式

遵循React状态不可变原则,使用map创建新数组,修改目标对象时返回新副本:

import { useState, useEffect } from 'react';

const Cart = () => {
  const [coba, setCoba] = useState([
    { name: "aditya" },
    { name: "joni" },
    { name: "surya", money: 2000 },
  ]);

  // 用useEffect包裹,确保仅挂载时执行一次
  useEffect(() => {
    setCoba(prevState => prevState.map(item => {
      if (item.name === "surya") {
        // 生成新对象,不修改原状态
        return { ...item, money: 500000 };
      }
      return item;
    }));
  }, []);

  console.log(coba);
  return <div>购物车示例</div>;
};

export default Cart;

逻辑说明

  • 使用map遍历原数组,返回全新数组,保证状态不可变性。
  • 找到目标对象时,通过{...item}复制原对象属性,再修改money,避免污染原状态。
  • 用useEffect包裹更新逻辑,空依赖数组确保只在组件挂载时执行一次,避免无限循环和严格模式下的重复执行问题。

内容的提问来源于stack exchange,提问作者adityamms

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 21:50:25