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

React Router中NavLink点击后URL变化但页面不加载问题求助

问题描述

构建了一个多页面React应用,导航栏包含“Login”“Signup”等认证相关NavLink。手动输入URL时页面可正常加载对应内容,但点击同URL的NavLink时,仅URL变化而页面不加载,甚至进入循环状态,需停止本地服务器并关闭标签页才能终止。

依赖版本:

  • react: ^18.2.0
  • react-router-dom: ^6.6.2

附相关代码:

App.js

import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import Navigation from "./components/Navigation";
import LogIn from "./pages/LogIn";
import Home from "./pages/Home"; // 补充原代码缺失的导入
import SignUp from "./pages/SignUp"; // 补充原代码缺失的导入

function App() {
  return (
    <>
      <BrowserRouter>
        <Navigation />
        <Routes>
          <Route index element={<Home />} />
          <Route path="/login" element={<LogIn />} />
          <Route path="/signup" element={<SignUp />} />
        </Routes>
      </BrowserRouter>
    </>
  );
}

export default App;
import { auth } from "../firebase";
import { signOut, onAuthStateChanged } from "firebase/auth";
import { toast } from "react-toastify";
import { NavLink, useNavigate } from "react-router-dom";
import { useState } from "react";

export default function Navigation() {
  const user = auth.currentUser;
  const [isLoggedIn, setIsLoggedIn] = useState();

  onAuthStateChanged(auth, (user) => {
    if (user) {
      setIsLoggedIn(true);
    } else {
      setIsLoggedIn(false);
    }
  });

  const navigate = useNavigate();

  const onLogOut = () => {
    signOut(auth)
      .then(() => {
        toast.success("You are signed out");
        console.log("You are signed out");
        navigate("/login");
      })
      .catch((error) => {
        toast.error("Something went wrong");
        console.log(error.message);
      });
  };

  return (
    <>
      <ul className="flex p-2 place-content-evenly">
        <li>BraThula</li>
        {!isLoggedIn && <NavLink to="login">Login</NavLink>}
        {!isLoggedIn && <NavLink to="signup">Signup</NavLink>}
        {isLoggedIn && (
          <li className="cursor-pointer" onClick={onLogOut}>
            logout
          </li>
        )}
        {isLoggedIn && <li className="">Hello, {user.name}</li>}
        {isLoggedIn && <li className="">Current User: {user.email}</li>}
      </ul>
    </>
  );
}

问题根源与修复方案

核心问题

  1. onAuthStateChanged监听未清理:在组件顶层直接调用onAuthStateChanged,每次组件渲染都会新增一个监听回调,导致回调堆积。用户状态变化时,多个回调触发setIsLoggedIn,引发组件反复渲染,最终形成循环。
  2. 用户信息读取错误:直接读取auth.currentUser无法实时同步用户状态,且Firebase用户的名称字段是displayName而非name,原代码存在字段错误。
  3. 相对路径跳转隐患:NavLink使用to="login"相对路径,在非根路径下可能导致跳转异常。

修复步骤

1. 用useEffect包裹监听并添加清理逻辑

通过useEffect注册监听,返回清理函数移除监听,避免回调堆积:

import { useEffect, useState } from "react";

export default function Navigation() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [user, setUser] = useState(null); // 用state存储实时用户信息

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      if (user) {
        setIsLoggedIn(true);
        setUser(user);
      } else {
        setIsLoggedIn(false);
        setUser(null);
      }
    });

    // 组件卸载时清理监听
    return unsubscribe;
  }, []); // 空依赖数组确保监听只注册一次

2. 修正用户信息读取逻辑

使用state中存储的user对象,同时修正Firebase用户名称字段:

// 原错误代码
{isLoggedIn && <li className="">Hello, {user.name}</li>}

// 修正后
{isLoggedIn && <li className="">Hello, {user.displayName}</li>}

3. 统一使用绝对路径跳转

将NavLink的to属性改为绝对路径,避免相对路径跳转异常:

{!isLoggedIn && <NavLink to="/login">Login</NavLink>}
{!isLoggedIn && <NavLink to="/signup">Signup</NavLink>}

完整修复后的Navigation.jsx

import { auth } from "../firebase";
import { signOut, onAuthStateChanged } from "firebase/auth";
import { toast } from "react-toastify";
import { NavLink, useNavigate } from "react-router-dom";
import { useState, useEffect } from "react";

export default function Navigation() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [user, setUser] = useState(null);
  const navigate = useNavigate();

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      if (user) {
        setIsLoggedIn(true);
        setUser(user);
      } else {
        setIsLoggedIn(false);
        setUser(null);
      }
    });

    return unsubscribe;
  }, []);

  const onLogOut = () => {
    signOut(auth)
      .then(() => {
        toast.success("You are signed out");
        console.log("You are signed out");
        navigate("/login");
      })
      .catch((error) => {
        toast.error("Something went wrong");
        console.log(error.message);
      });
  };

  return (
    <>
      <ul className="flex p-2 place-content-evenly">
        <li>BraThula</li>
        {!isLoggedIn && <NavLink to="/login">Login</NavLink>}
        {!isLoggedIn && <NavLink to="/signup">Signup</NavLink>}
        {isLoggedIn && (
          <li className="cursor-pointer" onClick={onLogOut}>
            logout
          </li>
        )}
        {isLoggedIn && <li className="">Hello, {user.displayName}</li>}
        {isLoggedIn && <li className="">Current User: {user.email}</li>}
      </ul>
    </>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 14:17:54