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

登录失败时意外跳转到首页的问题排查求助(附相关代码)

登录失败时意外跳转到首页的问题排查求助(附相关代码)

各位好,我现在遇到一个很奇怪的问题,想请大家帮忙看看:

我在做一个Expo + Expo Router的项目,登录功能里,当用户输入错误的用户名/密码时,后端(NextJS)会返回401状态码和错误信息。按我的代码逻辑,这时候应该进入!res.success分支:打印调试日志、把错误信息存到state里显示出来,不会做任何跳转操作。

现在的问题是:调试日志确实打印了,错误提示也能短暂显示,但紧接着页面就会莫名其妙跳转到首页。我翻遍了整个代码库,都没找到任何主动跳转到首页的代码,完全搞不懂为什么会触发这个跳转...

相关代码片段

1. LoginForm.jsx 中的登录处理逻辑

const handleLogin = async () => {
  const fieldErrors = validate();
  setErrors(fieldErrors);
  if (Object.keys(fieldErrors).length > 0) return;

  const res = await login(username, password);
  if (!res.success) {
    console.log("something is wrong")
    setErrors({ global: res.error });
  } else {
    router.replace("/(drawer)/dashboard");
  }
};

return(
  // ... 其他表单元素
  {errors.global && <Text style={styles.error}>{errors.global}</Text>}
)

2. AuthContext 完整代码(认证上下文)

import { createContext, useState, useEffect, useContext, useRef } from "react";
import * as SecureStore from "expo-secure-store";
import { useRouter } from "expo-router";
import { AppState } from "react-native";
import { BASE_URL } from "../constants/config";
import { setAuthHandlers } from "../utils/axiosInstance";

export const AuthContext = createContext(null);

export const AuthProvider = ({ children }) => {
  const [accessToken, setAccessToken] = useState(null);
  const [user, setUser] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const isRefreshing = useRef(false);
  const router = useRouter();

  // Store token securely
  const saveUser = async (user) => {
    setUser(user);
    await SecureStore.setItemAsync("user", JSON.stringify(user));
  };

  const saveToken = async (token) => {
    setAccessToken(token);
    await SecureStore.setItemAsync("accessToken", token);
  };

  const removeToken = async () => {
    setAccessToken(null);
    await SecureStore.deleteItemAsync("accessToken");
  };

  const login = async (username, password) => {
    setIsLoading(true);
    try {
      const res = await fetch(`${BASE_URL}/en/login/mobile/api`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        credentials: "include",
        body: JSON.stringify({ username, password }),
      });
      const data = await res.json();

      if (res.ok) {
        await saveToken(data.accessToken);
        await saveUser(data.user);
        return { success: true };
      } else {
        return { success: false, error: data.message || "Login failed" };
      }
    } catch (err) {
      return { success: false, error: "Something went wrong. Please try again.", };
    } finally {
      setIsLoading(false);
    }
  };

  const refreshToken = async () => {
    if (isRefreshing.current) return false;
    isRefreshing.current = true;
    try {
      const res = await fetch(`${BASE_URL}/en/refresh/mobile/api`, {
        method: "POST",
        credentials: "include",
      });
      const data = await res.json();
      if (res.ok) {
        await saveToken(data.accessToken);
        return true;
      }
      return false;
    } catch {
      return false;
    } finally {
      isRefreshing.current = false;
    }
  };

  const checkLoginStatus = async () => {
    setIsLoading(true);
    try {
      const token = await SecureStore.getItemAsync("accessToken");
      const storedUser = await SecureStore.getItemAsync("user");

      if (token) {
        setAccessToken(token);
        if (storedUser) {
          setUser(JSON.parse(storedUser));
        }
        const refreshed = await refreshToken();
        if (!refreshed) {
          await removeToken();
          setUser(null);
          router.replace("/(auth)/login");
        }
      } else {
        setUser(null);
        setAccessToken(null);
      }
    } catch (err) {
      console.error("Auth initialization error:", err);
    } finally {
      setIsLoading(false);
    }
  };

  useEffect(() => {
    setAuthHandlers({ refreshToken, });
  }, []);

  useEffect(() => {
    checkLoginStatus();
    const interval = setInterval(() => {
      if (accessToken) refreshToken();
    }, 10 * 60 * 1000);
    return () => clearInterval(interval);
  }, []);

  useEffect(() => {
    const sub = AppState.addEventListener("change", (state) => {
      if (state === "active" && accessToken) refreshToken();
    });
    return () => sub.remove();
  }, []);

  return (
    <AuthContext.Provider value={{
      accessToken, user, isLoading, login,
    }} >
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth must be used within an AuthProvider");
  }
  return context;
};

我自己的排查思路(没找到原因)

  • 确认了handleLogin里的else分支只有在登录成功时才会跳转到仪表盘,失败分支确实没有跳转代码;
  • 检查了AuthContext里的所有路由操作,只有checkLoginStatus里会跳转到登录页,没有跳首页的逻辑;
  • 怀疑过是不是Axios的拦截器?但我这里登录用的是原生fetch,Axios的拦截器是给其他请求用的,而且setAuthHandlers里也没写跳转逻辑;

有没有大佬能帮我分析下,这个莫名的首页跳转可能是哪里触发的?


内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 13:33:10