React中JWT校验完成前阻止路由渲染及useNavigate优化
问题原因
当前代码出现登录页闪屏,核心是3个逻辑漏洞:
- 缺少鉴权加载态:组件初始
authorized默认值为false,首次挂载时会直接走分支渲染<Outlet />,登录/注册等子路由会先被渲染,等异步JWT校验接口返回后才触发跳转,中间的等待时间就会出现页面闪屏。 - 鉴权函数调用位置错误:直接在组件渲染阶段调用
authorize(),每次组件重渲染都会重复发起鉴权请求,异步状态更新还会触发无意义的多次重渲染。 - 副作用逻辑位置错误:
navigate跳转属于副作用,直接写在渲染判断分支里会导致渲染流程异常,触发React警告甚至重复跳转。
额外说明:React Router 提供的useNavigate默认就是客户端路由跳转,本身不会触发整页重载,如果出现跳转重载,一般是错误使用了<a>标签做站内跳转、跳转时传入了带域名的全量地址触发浏览器默认导航、或者路由配置嵌套错误导致的。
修复方案
1. 重写ProtectedRoute组件
核心改动:
- 新增三态鉴权状态(加载中/已授权/未授权),初始状态为加载中,鉴权请求完成前只返回加载占位,不渲染任何子路由,从根源避免闪屏
- 把鉴权逻辑、跳转逻辑全部放到
useEffect中,仅在组件挂载、token变化时执行一次,避免重复调用接口 - 跳转时加
replace: true配置,避免已登录用户点击浏览器回退时再次回到登录页触发循环跳转
import { useState, useContext, useEffect } from "react"; import { useNavigate, Outlet } from "react-router-dom"; import AuthContext from "../Context/AuthProvider"; const ProtectedRoute = ({ access }) => { const [authStatus, setAuthStatus] = useState('loading'); const { auth } = useContext(AuthContext); const navigate = useNavigate(); useEffect(() => { const checkAuth = async () => { // 本地无token直接判定为未授权,不用发请求 if (!auth.accessToken) { setAuthStatus('unauthorized'); return; } try { await access(auth.accessToken); setAuthStatus('authorized'); } catch (err) { setAuthStatus('unauthorized'); } }; checkAuth(); }, [auth.accessToken, access]); useEffect(() => { if (authStatus === 'authorized') { navigate('/', { replace: true }); } }, [authStatus, navigate]); // 鉴权未完成时只显示加载态,可替换为项目全局加载组件、骨架屏 if (authStatus === 'loading') { return <div className="global-loading">校验登录状态中...</div>; } // 仅未授权状态下渲染登录/注册等子路由 if (authStatus === 'unauthorized') { return <Outlet />; } return null; }; export default ProtectedRoute;
2. useNavigate避免页面重载的注意事项
只要遵循以下规则,useNavigate只会做客户端无刷新路由切换:
- 站内跳转不要用原生
<a href="/xxx">标签,统一用React Router的<Link>组件或者navigate方法 - 站内跳转只传相对路径,比如写
navigate('/login'),不要写带域名的全量地址触发浏览器默认导航 - 不要重复嵌套同路径路由配置,避免路由匹配逻辑异常
补充说明
现有App.js中的路由配置不需要改动,替换ProtectedRoute组件后,JWT校验请求返回前不会渲染任何登录/注册页面,效果和之前EJS服务端中间件拦截完全一致:校验不通过才渲染对应路由,校验通过直接拦截跳转。
内容的提问来源于stack exchange,提问作者dislonely
相关产品推荐
相关产品推荐

