React报错:useNavigate()仅可在<Router>组件上下文使用及权限控制问题
问题根源
你遇到的useNavigate() may be used only in the context of a <Router> component报错,是因为useAuthentication Hook在<Router>组件渲染前就被调用了(App组件顶层调用),此时useNavigate无法获取到路由上下文。另外,原写法中即使未登录,受保护页面组件(如JobPage)仍会被初始化,导致后端数据请求提前触发。
解决方案
1. 创建路由守卫组件ProtectedRoute
这个组件会在路由层面拦截未登录请求,只有认证通过时才渲染目标页面,避免未登录时加载页面组件。
// components/ProtectedRoute.js import { Navigate } from 'react-router-dom'; import useAuthentication from '../utilities/useAuthentication'; const ProtectedRoute = ({ children }) => { const { isLoggedIn, isLoading } = useAuthentication(); // 认证请求未完成时显示加载状态 if (isLoading) { return <div>验证身份中...</div>; } // 已登录则渲染目标页面,否则跳转登录页 return isLoggedIn ? children : <Navigate to="/login" replace />; }; export default ProtectedRoute;
2. 修改useAuthentication Hook
移除useNavigate,让Hook专注于管理认证状态,添加加载状态处理异步请求过程:
// utilities/useAuthentication.js import { useState, useEffect } from 'react'; const useAuthentication = () => { const [isLoggedIn, setIsLoggedIn] = useState(false); const [userData, setUserData] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { const checkLoginStatus = async () => { try { const response = await fetch('http://localhost:5000/api/check-auth', { method: 'GET', credentials: 'include', }); if (response.ok) { const { userData } = await response.json(); setIsLoggedIn(true); setUserData(userData); } else { setIsLoggedIn(false); } } catch (error) { console.error('认证检查失败:', error); setIsLoggedIn(false); } finally { setIsLoading(false); } }; checkLoginStatus(); }, []); return { isLoggedIn, userData, isLoading }; }; export default useAuthentication;
3. 重构App.js路由配置
用ProtectedRoute包裹所有需要权限的页面:
// App.js import React from 'react'; import { BrowserRouter as Router, Route, Routes, } from 'react-router-dom'; import CustomNavbar from './components/common/Navbar'; import HomePage from './pages/HomePage'; import LoginPage from './pages/LoginPage'; import RegisterPage from './pages/RegisterPage'; import CreateJobPage from './pages/CreateJobPage'; import JobsPage from './pages/JobsPage'; import JobPage from './pages/JobPage'; import DashboardPage from './pages/DashboardPage'; import ProtectedRoute from './components/ProtectedRoute'; function App() { return ( <Router> <CustomNavbar /> <Routes> <Route path="/" element={<HomePage />} /> <Route path="/login" element={<LoginPage />} /> <Route path="/register" element={<RegisterPage />} /> <Route path="/create-job" element={<CreateJobPage />} /> <Route path="/jobs" element={<JobsPage />} /> {/* 受保护页面用ProtectedRoute包裹 */} <Route path="/job/:id" element={ <ProtectedRoute> <JobPage /> </ProtectedRoute> } /> <Route path="/dashboard" element={ <ProtectedRoute> <DashboardPage /> </ProtectedRoute> } /> </Routes> </Router> ); } export default App;
为什么这样有效
ProtectedRoute在<Router>内部渲染,调用useAuthentication时已处于路由上下文,彻底解决useNavigate报错。- 只有登录状态验证通过后,才会渲染受保护页面组件,避免未登录时触发后端数据请求。
- 添加
isLoading状态,避免在认证请求过程中出现页面闪烁或错误状态。
内容的提问来源于stack exchange,提问作者Jordan
相关产品推荐
相关产品推荐

