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

React useEffect依赖错误致函数重复执行7次问题排查

React DropdownMultiSelect 组件重复执行问题修复方案

核心问题原因

updateAddedState初始渲染阶段执行7次是依赖配置错误、冗余包装、无效逻辑共同导致的:

  • 占位符更新的useEffect错误将自身修改的placeholderValue加入依赖数组,触发「状态修改→effect触发→再次修改状态」的循环渲染链
  • 触发外部状态更新的useEffect同时依赖updateAddedState和addedItems,而updateAddedState被useCallback包裹时依赖父组件传入的updateState,如果父组件未对updateState做引用稳定处理,每次父组件重渲染都会生成新的updateState,连带触发当前组件effect重复执行
  • 代码中isMounted判断是无效逻辑:变量在每次effect执行时都会重新初始化,根本无法拦截卸载后的执行
  • addItem/removeItem方法采用手动数组拷贝、splice的写法,同时依赖闭包中旧的addedItems值,容易触发意外重渲染,且手动调用handleFilterData产生了冗余的状态更新
  • handleFilterData用useCallback包装后作为useEffect依赖,只要addedItems/data变化就会生成新的函数引用,额外触发effect执行

可落地修复方案

  1. 修正所有useEffect的依赖项,移除自引用、冗余依赖
  2. 去掉不必要的useCallback包装,减少引用变化带来的意外触发
  3. 用函数式状态更新替代闭包取值,避免旧值导致的逻辑异常
  4. 删除无效的isMounted判断和冗余的手动方法调用

核心修改后的代码片段

1. 修正占位符更新逻辑

// 移除依赖数组中的placeholderValue,避免自触发循环
useEffect(() => {
  if (addedItems.length === 1) {
    setPlaceholderValue(`${addedItems.length} vald`);
  } else if (addedItems.length > 1) {
    setPlaceholderValue(`${addedItems.length} valda`);
  } else {
    setPlaceholderValue(placeholder);
  }
}, [addedItems, placeholder]);

2. 简化过滤逻辑,移除冗余useCallback包装

// 删掉外层单独定义的handleFilterData useCallback,直接将逻辑写入effect
useEffect(() => {
  const newFilter = data
    .filter(item => item.name.toLowerCase().includes(searchTerm.toLowerCase()))
    .filter(item => !addedItems.some(added => added.id === item.id));
  setFilteredData(newFilter);
}, [searchTerm, addedItems, data]);

3. 修正外部状态同步逻辑,仅在addedItems变化时触发

// 删掉外层单独定义的updateAddedState useCallback,移除无效isMounted判断
useEffect(() => {
  console.log("running");
  updateState(addedItems);
  // 注意:如果父组件传入的updateState引用不稳定(未做useCallback包装),需要将updateState加入本effect的依赖数组
}, [addedItems]);

4. 修正增删选项的状态更新逻辑

// 用函数式更新拿最新状态,移除冗余的手动过滤调用
const addItem = (value: IData) => {
  setAddedItems(prev => [...prev, value]);
  setSearchTerm("");
  setInputActive(false);
};

// 简化数组过滤逻辑,避免双层循环+splice的下标偏移问题
const removeItem = (value: IData, e: React.MouseEvent) => {
  e.preventDefault();
  setAddedItems(prev => prev.filter(item => item.id !== value.id));
  setInputActive(true);
};

优化后效果

  • 初始渲染阶段console.log("running")仅执行1次,无无效重复触发
  • 只有addedItems实际发生增删变更时,才会调用父组件传入的updateState方法
  • 去掉冗余包装代码后,整体逻辑更简洁,重渲染次数大幅降低

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 18:45:42