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

ReactJS中forEach遍历给state赋值仅保留最后一项的原因及解决方法

问题成因
  • React 的状态更新是异步批量执行的:在 useEffect 回调的完整执行周期内,所有 setDisabledCategories 调用会被暂存,等到整个递归逻辑执行完成后才会统一触发更新。你在循环内每次调用 setDisabledCategories([...disabledCategories, category]) 时,拿到的 disabledCategories 始终是当前渲染周期的初始值(空数组),相当于每次都是往空数组里添加当前项,最终只有最后一次的更新结果会被保留。
  • 闭包捕获旧值问题:notActiveCategories 函数定义时捕获了当前渲染周期的 disabledCategories 初始值,递归过程中状态并没有实时更新,所以所有的状态更新操作都是基于同一个初始空数组执行的,没有叠加之前的更新结果。
解决方案

推荐优先使用「先批量收集符合条件的项,再一次性更新状态」的方案,性能更好,也避免闭包问题:

const [disabledCategories, setDisabledCategories] = useState([]);

// 递归收集所有禁用分类,返回完整的结果数组
function getDisabledCategories(categories) {
  const result = [];
  categories.forEach(category => {
    // 递归遍历子分类
    if (category.isActive && Array.isArray(category.children)) {
      result.push(...getDisabledCategories(category.children));
    }
    // 收集当前禁用分类
    if (!category.isActive) {
      result.push(category);
    }
  });
  return result;
}

useEffect(() => {
  const disabledList = getDisabledCategories(CategoriesData);
  setDisabledCategories(disabledList);
  console.log(disabledList); // 符合预期的3个分类项
}, []);

如果你需要在遍历过程中实时更新状态,可以使用 setState 的函数式更新形式,直接拿到最新的状态值进行修改:

function notActiveCategories(categories) {
  categories.forEach((category) => {
    if (category.isActive && Array.isArray(category.children)) {
      notActiveCategories(category.children);
    }
    if (!category.isActive) {
      // 函数式更新,prev 始终是最新的状态值
      setDisabledCategories(prev => [...prev, category]);
    }
  });
};

注意:该方案会多次触发状态重渲染,性能低于批量收集后更新的方案,非特殊场景不推荐使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 02:54:03