在 Firebase onAuthStateChanged 填充状态导致再次登录页面之前重定向到私有路由

Posted

技术标签:

【中文标题】在 Firebase onAuthStateChanged 填充状态导致再次登录页面之前重定向到私有路由【英文标题】:Redirected to private route before Firebase onAuthStateChanged populate state lead to sign in page again 【发布时间】:2020-10-03 18:35:12 【问题描述】:

登录时,重定向比来自onAuthStateChanged() 的状态更新更快。你如何处理这种行为?

// firebase/index.js
import React,  createContext  from 'react'
import * as firebase from "firebase/app";
import "firebase/firestore";
import "firebase/auth";

const firebaseConfig = 
    apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
    authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
    projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID
;

const app = firebase.initializeApp(firebaseConfig);
export default app

export const FirebaseContext = createContext()
export const FirebaseProvider = FirebaseContext.Provider
// index.js

import firebase,  FirebaseProvider  from './firebase'

            <FirebaseProvider value=firebase>
                <App />
            </FirebaseProvider>
// App.js

const App = () => 
    const firebase = useContext(FirebaseContext)
    const [auth, setAuth] = useState( loading: true, user: null )

    useEffect(() => 
        const unsubscribe = firebase.auth().onAuthStateChanged(function (user) 
            console.log(user)
            if (user) 
                console.log("logged in")
                setAuth( loading: false, user )
             else 
                console.log("not logged in")
                setAuth( ...auth, loading: false )
            
        );

        return () => 
            unsubscribe();
        
    , [])

    return (
        <BrowserRouter>
            ...

                <Switch>
                    <Route exact path="/signup" component=SignUp />
                    <Route exact path="/signin" component=SignIn />
                    <Route exact path="/signout" component=SignOut />
                    <Route exact path="/public" component=Public />
                    <PrivateRoute exact path="/private" auth=auth comp=Private />
// signin.js

import React,  useState, useContext  from 'react';
import  withRouter  from "react-router-dom";

import  FirebaseContext  from '../firebase'

const SignIn = ( history ) => 
    const [state, setState] = useState()
    const [alert, setAlert] = useState(false)

    const firebase = useContext(FirebaseContext)

    const signIn = () => 
        firebase
            .auth()
            .signInWithEmailAndPassword(state.email, state.password)
            .then(() => 
                history.push("/private")
            )
            .catch(function (error) 
                ...
            )
    

    return (
        ...
    )


export default withRouter(SignIn)
// PrivateRoute.js

const PrivateRoute = ( auth, comp: Component, ...rest ) => 
    return (
        <Route
            ...rest
            render=props => 
                auth.loading
                    ? <Loading on=true />
                    : !!auth.user
                        ? <Component ...props />
                        : <Redirect to="/signin" />
            
        />
    )

当我成功登录时(没有错误消息,并且网络 getAccountInfo 正在响应用户信息),history.push 正在重定向,但 PrivateRoute 再次将我重定向到 /signin 而不是 /private 路由.如果我手动更改 URL 并加载 /private 路由,则我已登录。

这是控制台:

// go to /private when not logged in -> redirected to /signin = OK

App.js: null
App.js: not logged in
PrivateRoute.js: loading: false, user: null

// on /signin page, fill form and submit

PrivateRoute.js: loading: false, user: null
App.js: P N: Array(0), l: ... 
App.js: logged in

// go to /private by typing it in the browser

PrivateRoute.js: loading: true, user: null
App.js: P N: Array(0), l: ... 
App.js: logged in
PrivateRoute.js: loading: false, user: P  ...  <-- the /private page is now rendered

我了解,成功登录后,我会在 onAuthStateChanged() 有时间填充 auth 状态之前被重定向。

我应该如何处理?我尝试了很多解决方案都没有成功:(

【问题讨论】:

您是否尝试阻止重定向以在加载状态下登录页面? 是的,但是我陷入了与加载错误的 PrivateRoute 相同的情况。或者我错过了什么? 【参考方案1】:

useEffect 将始终在第一次渲染后运行,因此您将始终陷入注销状态,从而导致登录和私人页面之间的快速重定向。您应该将用户身份验证数据保存在 localStorage 上,并在渲染前立即使用(不要在 useEffect 上设置);这样如果用户已经通过身份验证,您就不需要每次访问都获取此数据。

const [auth, setAuth] = useState( loading: true, user: localStorage.getItem('user'))

您还需要在注销或身份验证失败后清理 localStorage 以防止丢失重定向。

【讨论】:

以上是关于在 Firebase onAuthStateChanged 填充状态导致再次登录页面之前重定向到私有路由的主要内容,如果未能解决你的问题,请参考以下文章

在没有可可的情况下集成 Firebase 没有这样的模块“Firebase”?

在 Firebase 托管中使用 Firebase 和 React

在 Firebase 中部署功能时出现 Firebase 未找到错误

如何在firebase中获得firebase时间

升级到 firebase js sdk v8 后,在“firebase”中找不到导出“firestore”(导入为“firebase”)

Vue 2;在“firebase/app”中找不到导出“默认”(导入为“firebase”)