React中react-router与useNavigate登录后跳转首页失效问题
问题说明
登录成功后页面仅刷新,无法自动跳转至首页,手动访问首页功能正常;跳转其他页面(如/register)功能正常。预期效果为:登录成功后自动跳转到首页,登录接口返回数据存储在currentUser变量中,当currentUser有效时允许访问首页。
核心原因分析
AuthContext中useEffect依赖错误
原代码中useEffect依赖了login函数,组件每次渲染时login都会被重新创建,导致useEffect频繁触发,可能在currentUser未完全更新时就执行localStorage.setItem,干扰状态同步逻辑。ProtectedRoute状态感知不及时
App组件中直接获取currentUser并定义ProtectedRoute,当currentUser更新时,ProtectedRoute无法实时获取最新状态,导致跳转首页时仍判定用户未登录,触发重定向回/login,造成页面刷新的假象。登录函数错误处理缺失
login函数未处理请求失败场景,若接口返回错误,currentUser未被正确设置,但handleLogin仍会执行navigate("/"),此时ProtectedRoute因currentUser为null触发重定向。
修复方案
1. 修正AuthContextProvider的useEffect依赖
移除login作为useEffect的依赖,仅保留currentUser,并增加空值判断避免无效存储:
export const AuthContextProvider = ({ children }) => { const [currentUser, setCurrentUser] = useState( JSON.parse(localStorage.getItem("users")) || null ); const login = async (inputs) => { try { const res = await axios.post("https://micacarballo-social-media-api.onrender.com/api/v1/auth/login", inputs); setCurrentUser(res.data); return res.data; } catch (err) { throw err; } }; useEffect(() => { if (currentUser) { localStorage.setItem("users", JSON.stringify(currentUser)); } }, [currentUser]); const updateUser = async () =>{ const user = JSON.parse(localStorage.getItem("users")); if (!user?.token) return; const res = await axios.get("https://micacarballo-social-media-api.onrender.com/api/v1/users/me/",{ headers: { Authorization: `jwt ${user.token}` } }); setCurrentUser(res.data); }; return ( <AuthContext.Provider value={{ currentUser, login , updateUser}}> {children} </AuthContext.Provider> ); };
2. 重构ProtectedRoute为独立组件
将ProtectedRoute抽离为独立组件,确保每次渲染都能获取最新的currentUser:
// 单独创建ProtectedRoute.js文件 import { AuthContext } from './context/authContext'; import { Navigate } from 'react-router-dom'; export const ProtectedRoute = ({ children }) => { const { currentUser } = useContext(AuthContext); if (!currentUser) { return <Navigate to="/login" replace />; } return children; };
然后在App组件中使用该组件:
import { ProtectedRoute } from './ProtectedRoute'; import { createBrowserRouter, RouterProvider, Outlet } from 'react-router-dom'; function App() { const Layout = () => { return( <div> <Navbar /> <div style={{ display: "flex" }}> <LeftBar /> <div style={{ flex: 6 }}> <Outlet /> </div> </div> </div> ) }; const router = createBrowserRouter([ { path: "/", element: ( <ProtectedRoute> <Layout/> </ProtectedRoute> ), children :[ { path:"/", element: <Home /> }, { path:"/profile/:id", element:<Profile /> } ] }, { path: "/login", element: <Login /> }, { path: "/register", element: <Register /> } ]); return ( <div className="App"> <RouterProvider router={router} /> </div> ) } export default App
3. 确保AuthContextProvider正确包裹应用入口
在index.js中确认AuthContextProvider包裹了App组件,保证全局状态可被路由组件访问:
import React from 'react'; import ReactDOM from 'react-dom/client'; import { AuthContextProvider } from './context/authContext'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <AuthContextProvider> <App /> </AuthContextProvider> );
验证逻辑
登录成功后,login函数会正确设置currentUser并返回数据,handleLogin中await login(inputs)确保状态更新完成后再执行navigate("/");ProtectedRoute实时获取最新的currentUser,允许访问首页,不会触发重定向。
内容的提问来源于stack exchange,提问作者Mica Carballo

