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

React StrictMode下setState调用两次,如何避免数组状态重复更新?

问题解答

核心问题:StrictMode下状态更新重复执行的原因

StrictMode在开发模式下会刻意重复调用状态更新函数、副作用函数等,目的是检测代码中存在的非幂等操作(即执行多次会产生不同结果的逻辑)。你的setTimelineData回调里的操作之所以会重复添加元素,是因为你直接修改了原状态中的对象(lastRow.actions.push(newAction)修改了prevTimelineData里的原对象),加上更新函数被调用两次,导致两次push都生效。

解决方法:保持状态更新的幂等性

要解决这个问题,关键是永远不要直接修改原状态对象/数组,而是创建全新的副本进行修改,确保每次状态更新都是纯函数(输入相同,输出相同)。

修改你的setTimelineData逻辑

把直接修改lastRow.actions的操作改成创建新数组:

setTimelineData((prevTimelineData) => {
  const lastRow = prevTimelineData[prevTimelineData.length - 1];
  
  if (lastRow && lastRow.actions.length === 0) {
    // 创建新的actions数组,而非修改原数组
    const updatedLastRow = {
      ...lastRow,
      actions: [...lastRow.actions, newAction]
    };
    // 创建新的timelineData数组,替换最后一项
    return [...prevTimelineData.slice(0, -1), updatedLastRow];
  } else {
    const newRow = {
      id: (parseInt(lastRow.id) + 1).toString(),
      actions: [newAction]
    };
    // 保持创建新数组添加新项的逻辑
    return [...prevTimelineData, newRow];
  }
});

为什么原来的push会引发问题?

  • push是原地修改数组的方法,会直接改变原状态对象的引用。React依赖状态引用的变化来触发重新渲染,原地修改可能导致React无法准确检测到状态变化,引发渲染不一致的bug。
  • 在StrictMode下,更新函数被调用两次,原地修改会导致两次修改都作用在同一个原对象上,最终出现重复添加的情况;而使用纯函数式的更新(创建新副本),两次调用更新函数的输入都是相同的prevTimelineData,输出也会完全相同,React会自动合并重复的更新,不会产生重复元素。

开发模式下修改数组/对象的最佳实践

  • 永远不直接修改原状态:使用spread运算符(...)、map、filter、slice等返回新数组/对象的方法,替代push、pop、splice、直接修改对象属性等原地操作。
  • 使用函数式更新:当新状态依赖于旧状态时,一定要用setState(prevState => newState)的形式,避免闭包捕获旧状态值的问题,同时确保更新逻辑是幂等的。
  • 保持更新函数的纯性:更新函数内部不要有副作用(比如修改外部变量、调用异步API),只专注于根据旧状态计算新状态。

关于StrictMode的意义

StrictMode不是“阻碍”,而是提前暴露潜在bug的工具。它触发的重复执行,是为了让你发现代码中不符合React状态更新规则的逻辑——如果你的更新逻辑在执行两次后产生错误结果,说明代码本身就存在隐患(比如生产环境中可能因为异步操作、状态合并等问题出现类似的重复执行或状态不一致)。

禁用StrictMode只是暂时规避问题,并非根本解决。正确的做法是按照React的状态更新规则修改代码,这样无论是否开启StrictMode,代码都能稳定运行。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:40:24