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

React中useEffect内两次API调用在Safari刷新时仅一次完成的原因

Safari刷新时useEffect中仅一次API调用完成的问题排查与解决

可能的原因

  • Safari激进的GET请求缓存:Safari对GET请求的缓存策略比其他浏览器更严格,页面刷新时,若第二个API的响应头允许缓存,Safari可能直接读取本地缓存而不发起新请求,导致你在网络面板看不到请求记录,但实际缓存数据可能不符合预期。
  • Token初始化时机延迟:刷新页面时,AccountContext中的token可能还未完成初始化(比如从localStorage读取token的逻辑是异步的),导致第二个请求发送时token为空。如果后端对无token的请求返回200状态码但空数据,你的错误判断!belt_response.ok不会触发,就会出现请求“完成”但无有效数据的情况,且无错误提示。
  • 页面刷新时的异步请求中断:Safari在页面刷新过程中,若第二个请求还在pending状态,浏览器可能会终止该请求,这种中断不会被常规的catch捕获(除非用AbortController监听),导致看起来请求没执行完成。

解决方案

  1. 强制绕过缓存
    给第二个GET请求添加时间戳参数,强制Safari发起新请求:

    const belt_response = await fetch(`/api/belts/all?t=${Date.now()}`, {
        method: "GET",
        headers: { token }
    })
    

    也可以让后端设置响应头禁用缓存:Cache-Control: no-cache, no-store, must-revalidate

  2. 确保Token就绪后再请求

    • 将token加入useEffect的依赖数组,确保token更新时重新执行请求:
      useEffect(() => {
          // fetchData函数
      }, [token])
      
    • 在fetchData开头判断token是否存在,避免无效请求:
      async function fetchData() {
          if (!token) return;
          // 后续请求逻辑
      }
      
  3. 用AbortController管理请求生命周期
    避免页面卸载时请求被意外中断,同时捕获中断错误:

    useEffect(() => {
        const controller = new AbortController();
        const signal = controller.signal;
    
        async function fetchData() {
            try{
                const grading_response = await fetch(`/api/examination/grading/${grading_id}`, {
                    method: "GET",
                    headers: { token },
                    signal
                })
                
                if (!grading_response.ok) throw new Error("请求失败")
                const grading_data = await grading_response.json()
                setGrading(grading_data)
    
                const belt_response = await fetch("/api/belts/all", {
                    method: "GET",
                    headers: { token },
                    signal
                })
                
                if (!belt_response.ok) throw new Error("请求失败")
                const belt_data = await belt_response.json()
                console.log("Belt data:", belt_data)
                
            } catch (error) {
                if (error.name !== 'AbortError') {
                    console.error("请求出错:", error)
                }
            }
        }
        fetchData()
    
        return () => controller.abort();
    }, [token])
    

关于useEffect的理解

你的useEffect用法本身没有偏差,空依赖数组确实会让回调只在组件挂载时执行一次。问题出在Safari的特殊行为和上下文数据的初始化时机,并非对useEffect的概念误解。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 12:52:48