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

使用useCallback更新对象数组时出现追加而非更新的问题

React中使用useCallback更新数组对象时出现追加元素而非更新的问题

尝试用useCallback更新包含对象的数组,但实际效果是追加元素而非更新目标对象,以下是代码示例:

const items = [
  {
    id: 101,
    name: 'Mae Jemison',
  },
  {
    id: 201,
    name: 'Ellen Ochoa',
  },
];
const [headings, setHeadings] = useState(items);

const handleHeadingTextChange = useCallback(
  (value, id) => {
    let items2 = headings;
    items2 = items2.map((item, key) => {
      if (items2[key].id == id) {
        items2[key].name = value
      }
      return items2
    });

    setHeadings((prevState) => [...prevState, items2]) // 追加而非更新
    //setHeadings((prevState) => [...prevState, ...items2]) // 尝试后仍追加
  },
  [setHeadings],
);

<input type="text" id="101" value="" onChange={handleHeadingTextChange} />

预期输出:

items = [
  {
    id: 101,
    name: 'Johaan',
  },
  {
    id: 201,
    name: 'Ellen Ochoa',
  },
];

实际输出:

items = [
  {
    id: 101,
    name: 'Johaan',
  },
  {
    id: 201,
    name: 'Ellen Ochoa',
  },
  [{
    id: 101,
    name: 'Johaan',
  },
  {
    id: 201,
    name: 'Ellen Ochoa',
  }]
];

问题分析

你的代码存在三个核心问题:

  1. 直接修改原状态:items2[key].name = value直接修改了原数组中的对象,违反React状态不可变原则,可能导致状态更新异常。
  2. map函数返回错误:map循环中返回的是整个数组items2,最终得到的是一个包含多个原数组的新数组,而非更新后的元素集合。
  3. setHeadings逻辑错误:[...prevState, items2]是把处理后的数组作为单个元素追加到原数组末尾,而非替换原数组。

修正方案

方案1:基于当前状态创建新数组(需添加headings到依赖项)

const handleHeadingTextChange = useCallback(
  (value, id) => {
    // 遍历数组,仅更新匹配id的对象,返回新数组
    const updatedHeadings = headings.map(item => {
      if (item.id === id) {
        // 创建新对象,不修改原对象
        return { ...item, name: value };
      }
      // 不匹配的元素直接返回原对象
      return item;
    });
    // 直接设置新数组替换原状态
    setHeadings(updatedHeadings);
  },
  [headings, setHeadings] // 依赖项需包含headings
);

方案2:使用函数式更新(无需依赖headings)

利用setState的函数式更新,直接基于前一次状态处理,避免把headings加入依赖项:

const handleHeadingTextChange = useCallback(
  (value, id) => {
    setHeadings(prevState => {
      return prevState.map(item => {
        if (item.id === id) {
          return { ...item, name: value };
        }
        return item;
      });
    });
  },
  [setHeadings] // 仅依赖setHeadings
);

修正input的onChange绑定

原input的onChange回调参数是事件对象,需要调整为传递输入值和目标id,同时绑定对应的值使其成为受控组件:

<input 
  type="text" 
  id="101" 
  value={headings.find(item => item.id === 101).name} 
  onChange={(e) => handleHeadingTextChange(e.target.value, 101)} 
/>

关键注意点

  • 永远遵守React状态不可变原则:不要直接修改原状态对象/数组,必须创建新的对象/数组来更新状态。
  • map函数的作用是转换数组中的每个元素,需返回单个元素而非整个数组。
  • 更新数组状态时,直接返回完整的新数组,而非将新数组作为元素追加到原数组中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 06:55:21