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

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

执行日志截图

执行日志截图


原因分析

  1. React Strict Mode 触发重复执行:开发环境下,React Strict Mode会强制重复执行useEffect等生命周期方法,用来检测潜在的代码问题。这会让挂载阶段的useEffect多跑一次,是额外触发的主要原因。
  2. 异步请求竞态:第一次请求发起后还未完成时,组件因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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 23:27:32