React Router v6配合Firebase v9登录后无法跳转主页且抛状态更新警告
问题解决步骤
错误原因
- 自定义Hook
useAuth缺少加载状态标识:onAuthStateChanged是异步触发的,首次渲染时currentUser初始值为undefined,无法区分「还在加载鉴权状态」和「用户未登录」两种场景,导致RequireAuth组件直接触发跳转到登录页,组件被提前卸载。 - 鉴权状态回调执行时,
RequireAuth已经被卸载,此时尝试更新组件内部的currentUser状态,就会触发控制台的内存泄漏警告。 - 之前尝试的异步私有路由写法不符合React Hook使用规则:Hook只能在组件顶层作用域调用,不能放在
async函数内部,也不能用await接收Hook返回值。 - 不应该在
App组件内部定义RequireAuth组件,每次App重新渲染都会重建RequireAuth,导致不必要的组件重新挂载。
修复方案
1. 修改useAuth自定义Hook,新增加载状态
在firebase.js的useAuth中新增loading状态,标识鉴权状态是否加载完成:
export function useAuth() { const [currentUser, setCurrentUser] = useState(); // 新增加载状态,初始为true const [loading, setLoading] = useState(true); useEffect(() => { const unsub = onAuthStateChanged(auth, user => { setCurrentUser(user); // 拿到用户状态后设置加载完成 setLoading(false); }); return unsub; }, []) // 返回对象包含用户信息和加载状态 return { currentUser, loading }; }
2. 改造RequireAuth组件,移到App组件外部定义
加载完成前先返回占位内容,避免提前跳转:
// 将RequireAuth提到App组件外部定义 import { Navigate, Outlet } from "react-router-dom"; import { useAuth } from "./firebase"; function RequireAuth() { const { currentUser, loading } = useAuth(); // 鉴权状态未加载完成时返回加载提示 if (loading) return <div>鉴权中...</div>; // 加载完成后判断是否登录,未登录才跳转 if (!currentUser) { return <Navigate to="/login"/>; } return <Outlet /> } function App() { const [mainSection, setMainSection] = useState("home"); const d = new Date(); const time = d.getHours(); if (time >= 18 || time < 5) { document.documentElement.classList.add("dark"); } return ( <BrowserRouter> <Routes> <Route element={<RequireAuth/>}> <Route path="/" element={<Home mainSection={mainSection} setMainSection={setMainSection} />}/> </Route> <Route path="/login" element={<Login />} /> <Route path="/signup" element={<Signup />} /> </Routes> </BrowserRouter> ); } export default App;
3. 登录页添加成功跳转逻辑
在Login组件中调用登录接口成功后,用useNavigate跳转到主页即可:
import { useNavigate } from 'react-router-dom'; import { login } from '../firebase'; function Login() { const navigate = useNavigate(); // 表单提交触发的登录逻辑示例 const handleSubmit = async (e) => { e.preventDefault(); const email = e.target.email.value; const password = e.target.password.value; try { await login(email, password); // 登录成功跳主页 navigate('/'); } catch (err) { // 登录失败提示错误,留在当前页 alert('登录失败:' + err.message); } } // 剩余组件逻辑省略 }
内容的提问来源于stack exchange,提问作者outofthegravity
相关产品推荐
相关产品推荐

