React Router v6中PersistLogin组件在首页未触发的问题排查
问题:访问首页时PersistLogin组件未触发的原因及解决办法
我想要实现一个PersistLogin组件,用于验证用户是否持有有效的refresh token,若有效则获取新的access token。但用户访问网站首页('/')时该组件未触发,请问原因是什么?
路由配置代码
const Routes = () => { const { user } = useAuth(); return ( <Router> <Route index element={<Home />} /> <Route path='auth-form' element={<AuthForm />}> <Route path='register' element={<Register />} /> <Route path='login' element={<Login />} /> </Route> {/* PersistLogin组件所在路由 */} <Route element={<PersistLogin />}> {user && ( <> {user.isDev ? ( <> {DEV_NAVIGATION.map((nav) => ( <Route key={nav.path} path={`${nav.path}`} element={nav.component} /> ))} </> ) : ( <> {RECRUITER_NAVIGATION.map((nav) => ( <Route key={nav.path} path={`${nav.path}`} element={nav.component} /> ))} <Route path='/*' element={<Navigate to='/' replace={true} />} /> </> )} </> )} </Route> <Route path='/*' element={<Navigate to='/' replace={true} />} /> </Router> ); };
PersistLogin.tsx代码
const PersistLogin = () => { const [isLoading, setIsLoading] = useState(true); const refresh = useRefreshToken(); const { access_token } = useAuth(); useEffect(() => { const verifyRefreshToken = async () => { try { await refresh(); } catch (e) { console.log(e); } finally { setIsLoading(false); } }; !access_token ? verifyRefreshToken() : setIsLoading(false); }, [access_token, refresh]); return <>{isLoading ? <p>...Is loading</p> : <Outlet />}</>; }; export default PersistLogin;
原因分析
- 路由层级不包含首页:首页路由
<Route index element={<Home />} />是Router的直接子路由,完全独立于PersistLogin组件所在的路由分支。React Router只会匹配渲染对应层级的路由,所以访问首页时根本不会进入PersistLogin的路由范围,自然不会触发组件逻辑。 - PersistLogin仅包裹用户登录后的路由:当前PersistLogin的路由下只渲染
user存在时的导航路由,首页不在这个嵌套结构里,不管用户是否登录,访问首页都不会经过PersistLogin组件。
解决办法
方案一:将首页路由嵌套到PersistLogin下
把首页路由放到PersistLogin的路由内部,让访问首页时必走PersistLogin的验证逻辑。修改后的路由结构如下:
const Routes = () => { const { user } = useAuth(); return ( <Router> {/* 让PersistLogin作为全局布局路由,包裹所有需要验证的页面 */} <Route element={<PersistLogin />}> <Route index element={<Home />} /> <Route path='auth-form' element={<AuthForm />}> <Route path='register' element={<Register />} /> <Route path='login' element={<Login />} /> </Route> {user && ( <> {user.isDev ? ( <> {DEV_NAVIGATION.map((nav) => ( <Route key={nav.path} path={`${nav.path}`} element={nav.component} /> ))} </> ) : ( <> {RECRUITER_NAVIGATION.map((nav) => ( <Route key={nav.path} path={`${nav.path}`} element={nav.component} /> ))} <Route path='/*' element={<Navigate to='/' replace={true} />} /> </> )} </> )} <Route path='/*' element={<Navigate to='/' replace={true} />} /> </Route> </Router> ); };
方案二:全局挂载PersistLogin组件
如果首页不需要验证,但需要触发refresh token的校验逻辑,可以把PersistLogin组件直接放到Router外面或者App的根组件中,而非仅作为路由的element。比如:
function App() { return ( <PersistLogin> <Router> {/* 原路由结构 */} </Router> </PersistLogin> ); }
这种方式需要调整PersistLogin组件,去掉<Outlet />,改为直接渲染子组件:
const PersistLogin = ({ children }: { children: React.ReactNode }) => { const [isLoading, setIsLoading] = useState(true); const refresh = useRefreshToken(); const { access_token } = useAuth(); useEffect(() => { const verifyRefreshToken = async () => { try { await refresh(); } catch (e) { console.log(e); } finally { setIsLoading(false); } }; !access_token ? verifyRefreshToken() : setIsLoading(false); }, [access_token, refresh]); return <>{isLoading ? <p>...Is loading</p> : children}</>; };
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

