You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 20:54:07