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

React状态更新异常:登录成功后跳转仍返回登录页问题求助

问题分析与解决方案

核心问题

App组件的token状态仅在组件初始化时从localStorage/sessionStorage读取一次。登录成功后你只把token存入了存储,但没有更新App组件的token状态,导致路由守卫判断时token仍为null,触发跳转回登录页;刷新页面时组件重新初始化,才会读取新存储的token,恢复正常。

具体修复步骤

1. 给Login组件传递更新token的回调

修改App组件,将setToken作为props传给Login,让登录成功后主动更新App的token状态:

function App() {
    const [token, setToken] = useState(
        localStorage.getItem("JWToken") || sessionStorage.getItem("JWToken")
    );

    return (
        <>
            <Routes>
                <Route path="/register" element={<Register />} />
                <Route
                    path="/login"
                    element={<Login token={token as string} setToken={setToken} />}
                />
                <Route
                    path="/dashboard"
                    element={token ? <Dashboard /> : <Navigate to="/login" />}
                />
                <Route
                    path="/tickets"
                    element={token ? <Tickets /> : <Navigate to="/login" />}
                />
                <Route
                    path="/administration"
                    element={token ? <Administration /> : <Navigate to="/login" />}
                />
            </Routes>
        </>
    );
}

2. 在Login组件中调用回调更新token

登录成功存入token后,立即调用setToken更新App的状态,这样路由守卫就能拿到最新的token:

// 更新props类型定义
function Login({ token, setToken }: { token: string; setToken: React.Dispatch<React.SetStateAction<string | null>> }) {
    // 原有状态和逻辑保持不变
    const [showPassword, setShowPassword] = useState(false);
    const [showDemoRoles, setShowDemoRoles] = useState(false);
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");
    const [rememberMe, setRememberMe] = useState(false);
    const navigate = useNavigate();
    const location = useLocation();

    const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
        e.preventDefault();
        const URL = "http://localhost:5033/auth/login";
        try {
            const res = await axios.post(URL, {
                email: email.toLowerCase(),
                password,
            });

            if (res.status === 200) {
                const newToken = res.data.token;
                // 存入对应存储
                if (rememberMe) {
                    localStorage.setItem("JWToken", newToken);
                } else {
                    sessionStorage.setItem("JWToken", newToken);
                }
                // 更新App的token状态
                setToken(newToken);
                // 跳转到仪表盘
                navigate("/dashboard");
            }
        } catch (error: any) {
            toast.error(error?.response.data.msg);
        }
    };

    // 组件其他渲染逻辑...
}

3. 可选:封装ProtectedRoute优化路由守卫

重复写token ? <Component /> : <Navigate to="/login" />不够优雅,可以封装通用的受保护路由组件:

function ProtectedRoute({ children }: { children: React.ReactNode }) {
    const token = localStorage.getItem("JWToken") || sessionStorage.getItem("JWToken");
    return token ? children : <Navigate to="/login" />;
}

之后在App组件中简化路由配置:

function App() {
    const [token, setToken] = useState(
        localStorage.getItem("JWToken") || sessionStorage.getItem("JWToken")
    );

    return (
        <>
            <Routes>
                <Route path="/register" element={<Register />} />
                <Route
                    path="/login"
                    element={<Login token={token as string} setToken={setToken} />}
                />
                <Route path="/dashboard" element={
                    <ProtectedRoute>
                        <Dashboard />
                    </ProtectedRoute>
                } />
                <Route path="/tickets" element={
                    <ProtectedRoute>
                        <Tickets />
                    </ProtectedRoute>
                } />
                <Route path="/administration" element={
                    <ProtectedRoute>
                        <Administration />
                    </ProtectedRoute>
                } />
            </Routes>
        </>
    );
}

4. 可选:监听storage变化(跨标签页同步)

如果需要支持多标签页登录状态同步,在App组件中添加storage监听:

useEffect(() => {
    const handleStorageChange = () => {
        const newToken = localStorage.getItem("JWToken") || sessionStorage.getItem("JWToken");
        setToken(newToken);
    };

    window.addEventListener("storage", handleStorageChange);
    return () => window.removeEventListener("storage", handleStorageChange);
}, []);

修复逻辑说明

  • React状态更新遵循单向数据流,子组件无法直接修改父组件状态,必须通过父组件传递的回调触发更新。
  • 登录成功后同步更新App的token状态,能让路由守卫实时感知登录状态变化,避免跳转拦截。
  • 封装ProtectedRoute可减少代码重复,让路由配置更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:29:55