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

React中如何调用依赖更新后状态的回调函数?——解决AccessToken刷新后回调使用旧状态的问题

Fixing the Stale Access Token Issue in Your useRequest Hook

我完全懂你遇到的这个坑——React的状态更新是异步的,所以当refresh里调用setAccessToken后立刻执行回调,回调里的request还是会用上一轮的旧accessToken,导致重试请求依然带着过期令牌失败。下面给你几个在自定义Hook场景下好用的解决办法:

方案1:直接把新AccessToken传给回调

既然我们在refresh里已经拿到了新鲜的accessToken,没必要等状态更新完成,直接把它作为参数传给回调函数,让request能直接用这个最新值。修改后的代码如下:

function useRequest() { 
  const [accessToken, setAccessToken] = useState(42); 
  const [refreshToken, setRefreshToken] = useState(7); 

  // 给request加个可选参数,支持传入临时令牌
  const request = async (url, tempToken) => { 
    const tokenToUse = tempToken || accessToken;
    try { 
      // 注意axios的配置项是小写的headers,不是Headers
      const res = await axios.get(url, { headers: { Auth: tokenToUse } }); 
      return res; // 别忘了返回响应结果给调用方
    } catch (error) { 
      // 用可选链避免error.response不存在时报错
      if (error.response?.status === 401) { 
        // 调用refresh时传入回调,让它把新令牌传回来
        await refresh((newToken) => request(url, newToken));
      } else {
        // 把其他错误抛出去,方便上层处理
        throw error;
      }
    } 
  } 

  const refresh = async (callback) => { 
    const res = await axios.get("myserver.com", { params: { refreshToken }}); 
    const newAccessToken = res.data.accessToken;
    setAccessToken(newAccessToken); 
    callback(newAccessToken); // 直接把新令牌传给回调
  } 

  return request; 
}

这个方法简单直接,完全绕开了状态更新的异步延迟问题,拿到新令牌就立刻用它重试。

方案2:用useRef保存最新令牌

如果不想修改request的参数,可以用useRef来维护一个始终指向最新accessToken的引用——ref的更新是同步的,不会有状态更新的延迟。这样request里直接取ref的值,就永远不会拿到旧令牌了:

function useRequest() { 
  const [accessToken, setAccessToken] = useState(42); 
  const [refreshToken, setRefreshToken] = useState(7); 
  // 用ref同步保存最新的accessToken
  const accessTokenRef = useRef(accessToken);

  // 当状态更新时,同步更新ref的值
  useEffect(() => {
    accessTokenRef.current = accessToken;
  }, [accessToken]);

  const request = async (url) => { 
    try { 
      const res = await axios.get(url, { headers: { Auth: accessTokenRef.current } });
      return res;
    } catch (error) { 
      if (error.response?.status === 401) { 
        await refresh();
        // 刷新后ref已经是最新令牌了,直接重试
        return request(url);
      } else {
        throw error;
      }
    } 
  } 

  const refresh = async () => { 
    const res = await axios.get("myserver.com", { params: { refreshToken }}); 
    setAccessToken(res.data.accessToken); 
    // 这里不需要回调,ref会自动同步状态
  } 

  return request; 
}

这个方案适合希望保持request函数接口简洁的场景,用ref作为状态的“实时镜像”。

小提醒

你原来的代码里写的是Headers,但axios的配置项里应该是小写的headers,这个小细节可能会导致授权头无法正确发送,记得修正哦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 06:47:56