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

React Router V6中使用<Outlet/>和useEffect刷新时登出问题求助

React Router V6 私有路由JWT验证异步问题解决

问题描述

在React Router V6的PrivateRoutes组件中,通过useEffect调用axios验证JWT Token时,由于axios的异步特性,组件会先使用auth的初始状态(logged: null)进行判断,导致页面刷新时直接跳转到登录页,而此时验证请求还未完成,后续返回的正确登录状态无法生效。另外,尝试在异步的verify函数中返回<Outlet/>或<Navigate/>也无法渲染内容,因为异步函数的返回值不会被React作为组件输出处理。

原代码

const PrivateRoutes = () => {
  const { auth, setAuth } = useContext(AuthContext);  // 认证状态

  const verify = async () => {
    Axios.defaults.withCredentials = true;
    const res = await Axios.get("http://localhost:3001/verify");

    const data = res.data;
    console.log("data from jwt", data);
    setAuth({
      logged: data.logged,
      id: data.user_id,
      name: data.name,
    });
  };

  useEffect(() => {
    verify();
  }, []);

  console.log("private", auth);

  return auth.logged ? <Outlet /> : <Navigate to="/login" />;
};

export default PrivateRoutes;

刷新时控制台输出顺序

private {logged: null, name: '', id: ''}
data from jwt {id: 3, name: 'mariela', logged: true}

无效的修改尝试

尝试在异步的verify函数中直接返回组件,但组件无渲染内容,因为异步函数的返回值无法被React识别为渲染输出:

const PrivateRoutes = () => {
  const { auth, setAuth } = useContext(AuthContext);

  const verify = async () => {
    Axios.defaults.withCredentials = true;
    const res = await Axios.get("http://localhost:3001/verify");

    const data = res.data;

    setAuth({
      logged: data.logged,
      id: data.usu_id,
      name: data.nombre,
    });

    if (data.logged) {
      return <Outlet />;
    } else {
      return <Navigate to="/login" />;
    }
  };

  useEffect(() => {
    verify();
  }, []);
};

export default PrivateRoutes;

正确解决方案

核心思路是添加一个加载状态,在验证请求完成前不进行路由判断,避免初始状态导致的错误跳转:

  1. 在PrivateRoutes组件中新增loading状态,标记验证请求是否完成
  2. 在verify函数中,请求完成后更新loading状态
  3. 渲染时先判断loading,完成后再根据auth状态决定渲染内容

修改后的代码:

import { useState, useEffect, useContext } from 'react';
import { Outlet, Navigate } from 'react-router-dom';
import { AuthContext } from './AuthContext'; // 根据你的实际路径调整
import Axios from 'axios';

const PrivateRoutes = () => {
  const { auth, setAuth } = useContext(AuthContext);
  const [loading, setLoading] = useState(true); // 新增加载状态

  const verify = async () => {
    Axios.defaults.withCredentials = true;
    try {
      const res = await Axios.get("http://localhost:3001/verify");
      const data = res.data;
      setAuth({
        logged: data.logged,
        id: data.user_id,
        name: data.name,
      });
    } catch (error) {
      // 验证失败(如Token过期、无效),设置为未登录状态
      setAuth({ logged: false, id: '', name: '' });
      console.error('Token验证失败:', error);
    } finally {
      // 无论请求成功或失败,都结束加载状态
      setLoading(false);
    }
  };

  useEffect(() => {
    verify();
  }, []);

  // 加载中时返回null(或自定义加载组件)
  if (loading) {
    return null; 
    // 也可以替换为加载组件:return <div>加载中...</div>;
  }

  // 加载完成后根据auth状态判断路由
  return auth.logged ? <Outlet /> : <Navigate to="/login" replace />;
};

export default PrivateRoutes;

说明

  • 加载状态loading确保组件在验证请求完成前不会进行路由跳转,避免初始auth状态导致的错误登出
  • 加入try/catch捕获验证请求的异常,处理Token过期、无效等情况,保证状态正确更新
  • replace属性添加到<Navigate>,避免用户点击返回按钮回到验证过程中的页面

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 19:25:28