React Disney+克隆项目使用useHistory做权限控制出现跳转闪回问题
问题原因
- 缺少全局路由权限拦截逻辑:所有路由默认公开访问,没有对未登录用户限制访问受保护页面(首页、详情页),也没有对已登录用户限制访问登录页
- 没有监听Firebase的授权状态变化:页面刷新后Redux中的用户状态会清空,但Firebase默认会持久化用户登录态,当前代码没有在应用初始化时主动获取Firebase的登录状态同步到Redux
- 缺少授权状态加载态:初始渲染时授权状态还未确定就直接渲染路由,会先触发未登录逻辑跳转到登录页,等Firebase异步返回已登录状态后又跳回首页,就出现了登录页短暂闪烁的问题
修复方案
1. 封装受保护路由组件
新建ProtectedRoute.js,统一处理未登录用户的拦截逻辑:
import { Route, Redirect } from 'react-router-dom'; import { useSelector } from 'react-redux'; import { selectUserName } from '../../features/user/userSlice'; const ProtectedRoute = ({ component: Component, ...rest }) => { const userName = useSelector(selectUserName); return ( <Route {...rest} render={props => userName ? ( <Component {...props} /> ) : ( <Redirect to="/login" /> ) } /> ); }; export default ProtectedRoute;
2. 在Header中添加Firebase授权状态监听和加载态
修改Header.js,添加useEffect监听auth状态变化同步到Redux,同时新增加载状态避免路由提前跳转:
import React, { useEffect, useState } from 'react' import {selectUserName, selectUserPhoto, setUserLogin, setUserSignOut} from '../../features/user/userSlice' ; import { useDispatch, useSelector } from "react-redux" ; import { auth, provider} from "../../firebase" import { useHistory} from 'react-router-dom'; const Header = () => { const dispatch = useDispatch() const userName = useSelector(selectUserName); const userPhoto = useSelector(selectUserPhoto); const history = useHistory(); // 新增授权状态加载态 const [authLoading, setAuthLoading] = useState(true); useEffect(() => { // 监听Firebase授权状态变化 const unsubscribe = auth.onAuthStateChanged(user => { if (user) { // 已登录,同步用户信息到Redux dispatch(setUserLogin({ name: user.displayName, email: user.email, photo: user.photoURL })) } else { // 未登录,清空Redux用户信息 dispatch(setUserSignOut()) } setAuthLoading(false); }) return () => unsubscribe(); }, [dispatch]) // 授权状态加载中可返回加载占位,也可返回null if (authLoading) return null; const signIn = () => { auth.signInWithPopup(provider) .then((result) => { let user = result.user; dispatch(setUserLogin({ name: user.displayName, email: user.email, photo: user.photoURL })) history.push('/'); }) } const signOut = () => { auth.signOut() .then(() => { dispatch(setUserSignOut()); history.push('/login'); }) } // 补充你原有Header的JSX渲染逻辑 return ( // 原有Header结构 ) }
3. 修改Login组件,限制已登录用户访问
在Login组件中添加判断,已登录用户直接跳首页:
import { useSelector } from 'react-redux'; import { Redirect } from 'react-router-dom'; import { selectUserName } from '../../features/user/userSlice'; const Login = () => { const userName = useSelector(selectUserName); if (userName) { return <Redirect to="/" /> } // 补充你原有Login的JSX渲染逻辑 return ( // 原有登录页结构 ) }
4. 修改App.js路由配置,替换受保护路由
import ProtectedRoute from './components/ProtectedRoute'; // 引入封装的受保护路由 function App() { return ( <div className="App"> <Router> <Header /> <Switch> <Route path="/login"> <Login/> </Route> {/* 受保护路由用ProtectedRoute包裹 */} <ProtectedRoute path="/detail/:id" component={Detail}/> <ProtectedRoute path="/" component={Home}/> </Switch> </Router> </div> ); }
如果希望完全避免路由闪烁,可以把授权状态加载态提到App组件统一管理,等授权状态确认后再渲染路由内容。
内容的提问来源于stack exchange,提问作者lisovskiy329
相关产品推荐
相关产品推荐

