React中useEffect异步取数触发四次、重复加载的问题求助
问题:useEffect触发4次且重复加载数据
我在useEffect中通过异步方法加载数据,已传入所有必要依赖。按理解,useEffect应在组件挂载、首次渲染以及依赖变化时执行,预期触发3次:挂载时立即加载数据,首次渲染和依赖变化时不会进入if逻辑。但实际触发了4次,且数据被加载两次,无法排查原因。
相关代码片段
useEffect(() => { console.log('effect') if (ids.length === 0) { api.images.all().then((data) => { console.log(data); setIDs(data) }).catch(console.log) } }, [ids])
完整组件代码
//BuildIn import { useEffect, useState } from 'react' //Inside import api from '../services/api.service' import AsyncImage from '../components/AsyncImage.component' const ImagesPage = () => { const [ids, setIDs] = useState([]) useEffect(() => { console.log('effect') if (ids.length === 0) { api.images.all().then((data) => { console.log(data); setIDs(data) }).catch(console.log) } }, [ids]) return( <> {(ids.length > 0) ? ids.map((id, index) => <AsyncImage guid={id} key={index} />) : <div>No data</div>} </> ) } export default ImagesPage
执行日志截图

原因分析
- React Strict Mode 触发重复执行:开发环境下,React Strict Mode会强制重复执行useEffect等生命周期方法,用来检测潜在的代码问题。这会让挂载阶段的useEffect多跑一次,是额外触发的主要原因。
- 异步请求竞态:第一次请求发起后还未完成时,组件因Strict Mode触发重渲染,此时ids仍为空数组,就会再次进入if逻辑发起第二次请求,导致重复加载。
解决方案
1. 给请求添加取消逻辑(解决竞态)
利用AbortController在组件卸载或依赖变化时取消未完成的请求,避免重复请求:
useEffect(() => { console.log('effect') const controller = new AbortController(); const signal = controller.signal; if (ids.length === 0) { api.images.all({ signal }) // 需确保你的api支持传入AbortSignal .then((data) => { console.log(data); setIDs(data) }) .catch(err => { // 忽略取消请求的错误 if (err.name !== 'AbortError') console.log(err); }) } // 清理函数:取消未完成的请求 return () => controller.abort(); }, [ids])
2. 新增加载状态锁死请求
添加isLoading状态,确保同一时间只有一个请求在执行:
const ImagesPage = () => { const [ids, setIDs] = useState([]) const [isLoading, setIsLoading] = useState(false) useEffect(() => { console.log('effect') if (ids.length === 0 && !isLoading) { setIsLoading(true); api.images.all() .then((data) => { console.log(data); setIDs(data) }) .catch(console.log) .finally(() => setIsLoading(false)); } }, [ids, isLoading]) return( <> {isLoading ? <div>加载中...</div> : ( ids.length > 0 ? ids.map((id, index) => <AsyncImage guid={id} key={index} />) : <div>暂无数据</div> )} </> ) }
3. 临时移除Strict Mode(仅验证用)
如果只是想确认是否是Strict Mode导致的,可以临时移除项目根组件的<StrictMode>标签。注意:生产环境下Strict Mode不会生效,所以线上不会有这个问题,不推荐长期移除。
内容的提问来源于stack exchange,提问作者Pedro
相关产品推荐
相关产品推荐

