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

React+Firebase第三方登录后无法自动跳转至用户仪表盘问题

解决React + Firebase第三方登录后无法自动跳转受保护路由的问题

问题描述

使用React和Firebase实现受保护路由时,邮箱登录可正常跳转至Dashboard,但Google、Facebook等第三方登录后,页面会在用户认证状态更新前重定向回登录页;手动刷新页面后,可正常进入受保护的Dashboard页面。

原因分析

  1. 登录状态同步延迟:第三方登录(弹窗模式)的异步流程耗时稍长,Redux中currentUser状态更新慢于路由跳转时机,导致ProtectedRoute检测不到已登录用户,重定向回登录页。
  2. 本地loading状态冗余:Login组件使用本地loading状态而非Redux全局状态,且useEffect未监听该状态,无法在登录过程中正确触发跳转逻辑。
  3. ProtectedRoute无加载状态处理:登录过程中loading为true时,ProtectedRoute直接重定向,未等待currentUser状态更新完成。

解决方案

1. 优化ProtectedRoute,添加加载状态处理

修改protectedRoute.js,在登录加载过程中显示加载组件,而非直接重定向:

import React from 'react';
import { useSelector } from 'react-redux';
import LoadingToRedirect from './LoadingToRedirect';

const ProtectedRoute = ({children}) => {
  const { currentUser, loading } = useSelector((state) => state.user);
  
  // 登录过程中显示加载,避免提前重定向
  if (loading) {
    return <div>加载中...</div>; // 可替换为自定义加载组件
  }
  
  return currentUser ? children : <LoadingToRedirect />;
}

export default ProtectedRoute;

2. 重构Login组件,移除本地loading状态

删除Login组件中的本地loading状态,直接使用Redux全局状态,并优化useEffect的触发条件:

import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector} from "react-redux";
import { useNavigate, Link } from "react-router-dom";
import { fbSignInInitiate, googleSignInInitiate, loginInitiate } from '../../userauth/actions';

import './login.css';

const Login = () => {
  const [state, setState] = useState({
    email: "",
    password: "",
  });

  const { email, password } = state;

  // 从Redux获取全局状态
  const { currentUser, loading } = useSelector((state) => state.user);

  const navigate = useNavigate();
  const dispatch = useDispatch();

  // 仅当currentUser存在时跳转,依赖currentUser和navigate
  useEffect(() => {
    if (currentUser) {
      navigate("/dashboard");
    }
  }, [currentUser, navigate]);

  const handleGoogleSignIn = () => {
    dispatch(googleSignInInitiate());
  };
  
  const handleFBSignIn = () => {
    dispatch(fbSignInInitiate());
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    if(!email || !password) {
      return;
    }
    dispatch(loginInitiate(email, password));
    setState({ email: "", password: "" });
  };
  
  const handleChange = (e) => {
    let { name, value } = e.target;
    setState({...state, [name]: value });
  };
  
  return (
    <div>
      <div id="logreg-form">
        <form className='form-signin' onSubmit={handleSubmit}>
          <h1>Sign in</h1>
          <div className="social-login">
            <button 
              className='btn google-btn social-btn'
              type='button'
              onClick={handleGoogleSignIn}>
              <span>
                <i className='fab fa-google-plus-g'>Sign in with Google</i>
              </span>
            </button>
            <button 
              className='btn facebook-btn social-btn'
              type='button'
              onClick={handleFBSignIn}>
              <span>
                <i className='fab fa-facebook-f'>Sign in with Facebook</i>
              </span>
              </button>
          </div> 
          <p>OR</p>
          <input
          type="email"
          id="inputEmail"
          className='form-control'
          placeholder='Email Address'
          name="email"
          onChange={handleChange}
          value={email}
          required
          />
          <input
          type="password"
          id="inputPassword"
          className='form-control'
          placeholder='Password'
          name="password"
          onChange={handleChange}
          value={password}
          required
          />
          <button 
          className='btn btn-secondary btn-block' 
          type="submit">
            <i className="fas fa-sign-in-alt"></i>Sign In
          </button>
          <hr />
          <p>Don't have an account</p>
          <Link to="/signup">
            <button 
            className='btn btn-primary btn-block' 
            type="button" id="btn-signup">
              <i className='fas fa-user-plus'></i>Sign up New Account
            </button>
          </Link>
        </form>
      </div>
    </div>  
  )
}

export default Login;

3. 修复userReducer的logout状态处理

在reducer.js的LOGOUT_SUCCESS case中添加loading: false,避免logout后loading状态残留:

case types.LOGOUT_SUCCESS:
  return {
    ...state,
    currentUser: null,
    loading: false, // 新增:重置loading状态
  }

4. 优化注册流程的异步逻辑

修改Actions.js中的registerInitiate,确保updateProfile完成后再dispatch成功状态:

export const registerInitiate = (email, password, displayName) => {
    return async function (dispatch) {
        dispatch(registerStart());
        try {
            const { user } = await auth.createUserWithEmailAndPassword(email, password);
            await user.updateProfile({ displayName }); // 等待昵称更新完成
            dispatch(registerSuccess(user));
        } catch (error) {
            dispatch(registerFail(error.message));
        }
    }
};

修改后效果

第三方登录完成后,Redux状态更新时,Login组件会自动触发跳转至Dashboard;ProtectedRoute在登录加载过程中显示加载状态,避免提前重定向,确保用户状态同步完成后再进入受保护页面。

内容的提问来源于stack exchange,提问作者happypenguin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 23:06:32