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

React中API返回401响应触发登出时抛出TypeError错误

问题分析与解决方案

问题根源

  1. 异步请求延迟导致组件卸载后执行代码:当API返回401时调用logoutUser会触发页面导航,Profile组件被卸载,但之前发起的异步fetch请求的回调仍会继续执行,此时调用函数可能访问到已失效的上下文引用。
  2. logoutUser函数引用不稳定:AuthContext中每次provider渲染时都会重新创建logoutUser函数,Profile组件挂载时获取的是旧版本函数,当context更新后,旧函数可能出现引用失效问题。
  3. useEffect依赖缺失:getProfile依赖token和logoutUser,但useEffect的依赖数组为空,导致使用的是组件挂载时的旧值/旧函数。

解决方案

1. 标记组件挂载状态,避免卸载后执行代码

在Profile组件中用useRef跟踪组件是否挂载,执行关键逻辑前先做检查:

import { useRef, useEffect, useContext } from 'react';
import AuthContext from './AuthContext';

function Profile() {
  const { token, logoutUser } = useContext(AuthContext);
  const isMounted = useRef(true);
  const [profile, setProfile] = useState({});

  const getProfile = async () => {
    const response = await fetch("/profile/me/", {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        'Authorization': `Token ${token}`,
      },
    });

    // 组件已卸载则直接终止后续逻辑
    if (!isMounted.current) return;

    if (response.status === 401) {
      console.log("Should Log out");
      logoutUser();
    } else {
      const data = await response.json();
      setProfile(data);
    }
  };

  useEffect(() => {
    getProfile();
    // 组件卸载时更新标记
    return () => {
      isMounted.current = false;
    };
  }, [token, logoutUser]); // 添加依赖,确保使用最新的token和logoutUser

  // 组件其他代码...
}

2. 缓存logoutUser函数,保持引用稳定

在AuthContext中用useCallback包裹logoutUser,避免函数随provider渲染频繁重建:

import { useCallback } from 'react';

// AuthContext内部代码
const logoutUser = useCallback(() => {
  setToken(null);
  localStorage.removeItem('token');
  navigate("/");
}, [navigate]); // useNavigate返回的函数是稳定的,可安全作为依赖

const contextData = {
  loginUser: loginUser,
  token: token,
  logoutUser: logoutUser,
};

3. 取消未完成的fetch请求(可选)

用AbortController在组件卸载时取消未完成的请求,彻底避免无效回调:

const getProfile = async () => {
  const controller = new AbortController();
  const signal = controller.signal;

  try {
    const response = await fetch("/profile/me/", {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        'Authorization': `Token ${token}`,
      },
      signal: signal, // 关联取消信号
    });

    if (response.status === 401) {
      console.log("Should Log out");
      logoutUser();
    } else {
      const data = await response.json();
      setProfile(data);
    }
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log("Fetch aborted");
    } else {
      throw error;
    }
  }
};

useEffect(() => {
  getProfile();
  // 组件卸载时取消请求
  return () => controller.abort();
}, [token, logoutUser]);

为什么导航栏登出按钮正常?

导航栏按钮是在组件正常挂载状态下同步调用logoutUser,不存在异步延迟导致的组件卸载或引用失效问题,因此可以正常执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 19:37:03