React Router V6如何禁止直接访问子路由并重定向至父路由
问题与需求
现有React Router路由配置如下:
<Route path={`/home`} element={<PrivateRoute />}> <Route index={true} element={<Home />} /> <Route path={`settings`} element={<Settings />} /> </Route>
需求:禁止用户直接从浏览器地址栏访问/home/settings这类子路由,一旦直接访问需自动重定向到/home。该需求与身份认证无关,此前尝试用上下文存储访问路径的方案可维护性差,需要更合理的实现方式。
解决方案
方案一:路由守卫+会话存储标记(支持子路由刷新)
通过创建路由守卫组件,结合会话存储标记用户是否已访问过父路由/home,实现子路由访问控制:
- 创建路由守卫组件
import { useEffect } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; const HomeChildRouteGuard = ({ children }) => { const location = useLocation(); const navigate = useNavigate(); useEffect(() => { // 检查是否有来自/home的导航标记或会话存储标记 const hasVisitedHome = location.state?.fromHome || sessionStorage.getItem('hasVisitedHome'); if (!hasVisitedHome) { // 重定向到/home,避免循环重定向 navigate('/home', { replace: true, state: { fromRedirect: true } }); } }, [location, navigate]); return children; };
- 修改Home组件,添加访问标记
import { useEffect } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; const Home = () => { const location = useLocation(); const navigate = useNavigate(); useEffect(() => { // 首次进入/home时设置会话存储标记 if (!location.state?.fromRedirect) { sessionStorage.setItem('hasVisitedHome', 'true'); } // 离开/home时清除标记 return () => { sessionStorage.removeItem('hasVisitedHome'); }; }, [location]); // 导航到子路由时带上来源标记 const goToSettings = () => { navigate('/home/settings', { state: { fromHome: true } }); }; return ( <div> <h1>首页</h1> <button onClick={goToSettings}>进入设置</button> </div> ); };
- 更新路由配置,用守卫包裹子路由
<Route path={`/home`} element={<PrivateRoute />}> <Route index={true} element={<Home />} /> <Route path={`settings`} element={ <HomeChildRouteGuard> <Settings /> </HomeChildRouteGuard> } /> </Route>
方案二:导航类型判断(不支持子路由刷新)
如果不需要允许子路由刷新,可以通过导航类型判断直接访问行为:
import { useEffect } from 'react'; import { useLocation, useNavigate, useNavigationType } from 'react-router-dom'; const HomeChildRouteGuard = ({ children }) => { const location = useLocation(); const navigate = useNavigate(); const navigationType = useNavigationType(); useEffect(() => { // 如果是直接打开/刷新页面(POP类型)且无来源标记,重定向到/home if (navigationType === 'POP' && !location.state?.fromHome) { navigate('/home', { replace: true }); } }, [location, navigate, navigationType]); return children; };
之后同样将子路由用该守卫组件包裹即可,此方案下刷新子路由会被重定向回/home。
内容的提问来源于stack exchange,提问作者Shourya Shikhar
相关产品推荐
相关产品推荐

