React中依赖为空的useEffect导致列表重复项的解决方法
修复React列表重复数据问题
核心问题分析
- React 18严格模式触发重复执行:开发环境下,React 18的严格模式会让
useEffect执行两次,导致整个数据获取流程重复触发,最终数据翻倍。 - 异步状态更新时序冲突:
setProducts([])是异步操作,后续的子fetch请求回调可能在列表清空完成前就开始执行,或者两次流程的请求同时往列表中追加数据,最终造成重复。
具体修复方案
方案1:批量请求+请求取消(推荐)
改用Promise.all批量处理所有子请求,一次性更新状态,同时用AbortController取消未完成的请求,避免重复执行带来的问题:
const [products, setProducts] = useState([]); useEffect(() => { const abortController = new AbortController(); const signal = abortController.signal; fetch('http://localhost:1234/', { signal }) .then((response) => response.json()) .then((data) => { const productPromises = []; const maxItems = Math.min(10, data['products'].length); // 收集所有子请求的Promise for(let i = 0; i < maxItems; i++) { productPromises.push( fetch(`http://localhost:1234/lookup/${data['products'][i]}`, { signal }) .then(res => res.json()) ); } // 等待所有请求完成后一次性更新状态 Promise.all(productPromises) .then(results => { setProducts(results); }) .catch(err => { if (err.name !== 'AbortError') console.error(err); }); }) .catch(err => { if (err.name !== 'AbortError') console.error(err); }); // 清理函数:组件卸载或effect重新执行时取消所有未完成请求 return () => abortController.abort(); }, []);
方案2:临时关闭严格模式(仅开发环境验证用)
如果是开发环境严格模式导致的重复执行,可以暂时在项目入口文件(如index.js)中去掉<React.StrictMode>标签,但不推荐长期使用——严格模式能帮你提前发现潜在的组件问题。
关键优化点
- 批量更新状态:避免多次调用
setProducts追加数据,改用Promise.all一次性替换整个列表,彻底消除异步状态更新的时序冲突。 - 请求生命周期管理:用
AbortController确保组件卸载或重复渲染时,旧请求会被取消,不会再触发状态更新。 - 减少不必要的异步操作:去掉单独的
setProducts([]),直接用请求结果覆盖列表,避免额外的异步清空步骤。
内容的提问来源于stack exchange,提问作者Femn Dharamshi
相关产品推荐
相关产品推荐

