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

React路由内联if-else执行两次 重复重定向问题排查

问题根因
  • 异步鉴权和初始渲染存在时序差:isAuthenticated的初始值被设为false,组件首次挂载时,useEffect触发的后端token校验请求是异步逻辑,还未返回结果,这轮渲染会直接命中三元表达式的else分支,写在else里的日志、重定向逻辑会在这时候执行。等异步请求拿到结果,调用setIsAuthenticated(true)更新状态,组件触发第二轮重渲染,才会命中if分支渲染对应页面,因此会观察到两个分支先后触发。注意:两个分支不是同一次渲染中重复执行,是两次独立渲染分别命中了不同判断结果。
  • React 18+开发环境严格模式放大异常:通过官方脚手架新建的React 18项目,入口文件默认会用<StrictMode>包裹根组件,开发环境下会执行一次额外的挂载-卸载-重挂载流程,空依赖数组的useEffect会触发两次,导致鉴权请求重复发起、状态多更新一轮,进一步引发重复重定向的问题。
  • 代码写法不规范:路由的element属性要求传入合法React节点,原代码在dashboard路由的else分支直接写console.log,其返回值是undefined,本身就不符合React渲染规则;首次渲染未拿到鉴权结果就直接触发Navigate重定向,等状态更新后又要跳转回目标路由,自然会出现重复跳转的异常。
修复方案

核心是新增鉴权加载的中间状态,避免鉴权完成前就执行路由分支判断:

  1. 新增loading状态标识鉴权进度,初始值设为true,等鉴权接口返回结果(无论成功、失败),都将loading置为false,鉴权过程中统一渲染加载提示,不走路由判断逻辑。
  2. 修正所有路由分支的返回值,确保三元表达式的两个分支都返回合法React节点,禁止在element属性位置直接写无节点返回的执行语句。
  3. (非必要不推荐)如果不需要严格模式的开发校验,可以在项目入口文件移除<StrictMode>的包裹,该方式只能掩盖开发环境的重复执行问题,无法解决生产环境下初始渲染和异步鉴权的时序差问题。

修复后的核心代码

import React, { Fragment, useState, useEffect } from "react";
import './App.css';
import "react-toastify/dist/ReactToastify.css";
import {BrowserRouter as Router, Routes, Route, Navigate} from "react-router-dom";
import { toast } from "react-toastify";

//components
import Login from "./components/Login";
import Register from "./components/Register";
import Dashboard from "./components/Dashboard";
import Home from "./components/Home";
import Auction from "./components/Auction";

function App() {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  // 新增鉴权加载状态
  const [authLoading, setAuthLoading] = useState(true);

  const checkAuthenticated = async () => {
    try {
      const res = await fetch("http://localhost:5000/auth/is-verify", {
        method: "GET",
        headers: { token: localStorage.token }
      });
      const parseRes = await res.json();
      setIsAuthenticated(parseRes === true);
    } catch (err) {
      console.error(err.message);
      setIsAuthenticated(false);
    } finally {
      // 鉴权流程结束,关闭加载状态
      setAuthLoading(false);
    }
  };

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

  const setAuth = boolean => {
    setIsAuthenticated(boolean);
  };

  // 鉴权未完成时渲染加载提示,不执行路由判断
  if (authLoading) {
    return <div className="loading">权限校验中...</div>
  }
  
  return (
    <Fragment>
      <Router>
        <div className="container">
          <Routes>
            <Route path='/home' element = {
                  isAuthenticated ? (
                    <Home setAuth={setAuth}  />
                  ) : (
                    <Navigate to="/login" />
                  )
                } />
            <Route path='/login' element = {
                !isAuthenticated ? (
                  <Login setAuth={setAuth}  />
                ) : (
                  <Navigate to="/home" />
                )
              } />
            <Route path='/register' element = {
                !isAuthenticated ? (
                  <Register setAuth={setAuth}  />
                ) : (
                  <Navigate to="/home" />
                )
              } />
            <Route path='/dashboard' element = {
                isAuthenticated ? (
                  <Dashboard setAuth={setAuth}  />
                ) : (
                  <Navigate to="/login" />
                )
              } />
            <Route path='/auction' element = {<Auction setAuth={setAuth} />} />
          </Routes>
        </div>
      </Router>
    </Fragment>
  );
};

export default App;

优化建议:后续可以把鉴权判断逻辑抽离成通用的PrivateRoute、PublicRoute包装组件,不用在每个路由下重复写三元判断,可维护性更高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 12:16:01