react-router-dom私有路由刷新闪现/login页问题与路由优化咨询
问题根因
- 鉴权加载逻辑错误:Firebase 初始化验证用户登录状态是异步操作,在验证完成前你就直接根据初始的
auth空值判断跳转登录页,等验证完成拿到登录状态又跳回目标页,就出现了闪登录页的问题
- 鉴权加载逻辑错误:Firebase 初始化验证用户登录状态是异步操作,在验证完成前你就直接根据初始的
- 加载状态逻辑写反:
logging(实际是loading含义)初始值为null,且onAuthStateChanged回调里的状态设置逻辑错误,未登录时反而把loading设为true,导致鉴权过程中状态判断混乱
- 加载状态逻辑写反:
- 路由配置不合理:没有统一的私有路由拦截逻辑,用state存上次访问路径的方式刷新会重置,无法保留刷新前的页面路径
- 监听逻辑冗余:App组件中监听
onAuthChange的useEffect依赖了auth,每次auth变化都会重新注册监听函数,导致重复触发
- 监听逻辑冗余:App组件中监听
修复步骤
第一步:修正鉴权逻辑的状态设置
修改Storage/Auth.js里的onAuthChange函数:
const onAuthChange = (setAuth, setLoading) => { auth().onAuthStateChanged((u) => { // 回调触发代表鉴权完成,先把加载状态设为false setLoading(false); setAuth(u || null); }); };
第二步:修正App组件的状态和监听逻辑
修改App组件的初始状态和useEffect依赖:
function App() { const [user, setUser] = useState(null); const [auth, setAuth] = useState(null); // 初始加载状态设为true,代表正在鉴权 const [logging, setLogging] = useState(true); useEffect(() => { initFirebase(); }, []); // 去掉auth依赖,只在组件挂载时注册一次监听即可 useEffect(() => { onAuthChange(setAuth, setLogging); }, []); // ... 其余代码保持不变 }
第三步:封装私有路由高阶组件
在Routes组件里新增PrivateRoute组件,统一拦截需要鉴权的路由,同时记得从react-router-dom导入Switch组件:
// 私有路由组件,未登录时跳转到登录页 const PrivateRoute = ({ component: Component, ...rest }) => { const { auth, logging } = useContext(UserContext); return ( <Route {...rest} render={(props) => { // 鉴权中显示加载态,不做跳转 if (logging) return <div>加载中...</div>; // 已登录渲染对应组件,未登录跳登录页,同时携带当前路径,登录后可以直接跳回 return auth ? <Component {...props} /> : <Redirect to={{ pathname: "/login", state: { from: props.location } }} />; }} /> ); };
第四步:重构路由配置
把原来需要鉴权的路由换成PrivateRoute,去掉冗余的根路径重定向逻辑,修改RedirectPage组件逻辑:
const RedirectPage = () => { const { auth, logging } = useContext(UserContext); const history = useHistory(); // 鉴权中显示加载 if (logging) return <div>加载中...</div>; // 已经登录的用户访问登录页,直接跳转到之前访问的页面或者首页 if (auth) { const from = history.location.state?.from?.pathname || "/home"; return <Redirect to={from} />; } // 未登录显示登录组件 return <Login />; }; // 重构后的路由 return ( <Router> <Switch> <Route exact path="/login" component={RedirectPage} /> <PrivateRoute exact path="/home" component={Home} /> <PrivateRoute exact path="/dashboard" component={Dashboard} /> {/* 根路径重定向逻辑 */} <Route path="/" render={() => <Redirect to="/home" />} /> </Switch> </Router> );
第五步:修正Login组件的跳转逻辑
不要写死登录后跳转到/home,登录成功后跳转到用户之前要访问的页面:
const Login = () => { const [mail, setMail] = useState<string>(""); const [password, setPassword] = useState<string>(""); const { setUser } = useContext(UserContext); const history = useHistory(); const handleSignIn = async (m: string, p: string) => { await signIn(m, p).then((e) => { setUser(e); // 跳转到之前要访问的页面,默认跳/home const from = history.location.state?.from?.pathname || "/home"; history.push(from); }); }; const handleOnKey = (e: any) => { if (e.key === "Enter") { e.preventDefault(); handleSignIn(mail, password); } }; // ... 其余渲染代码保持不变 }
额外优化建议
可以删除Dashboard组件里的setPath相关逻辑,现在已经不需要用state保存访问路径了。
内容的提问来源于stack exchange,提问作者programandoconro
相关产品推荐
相关产品推荐

