无法在加载时获取 currentUser

IT技术 javascript firebase firebase-authentication
2021-03-12 15:07:42

尝试检查用户是否通过firebase.auth().currentUser以下方式登录时

if (firebase.auth().currentUser === null) {
  console.log('User not signed in');
}

每当我刷新页面或浏览上述内容时都会返回 null(即使我刚刚登录)。

奇怪的是,如果我登录

console.log(firebase.auth().currentUser) // This returns null
console.log(firebase.auth()) // Here I can inspect the object and currentUser exists...!

我真的不知道这里发生了什么。我正在使用 React 和 Redux,但我想说这并不重要。

firebase 初始化是否有一点延迟,您无法访问 currentUser?如果是这样,我如何在 的日志输出中看到它firebase.auth()

6个回答

这是一个常见的问题。 https://firebase.google.com/docs/auth/web/manage-users 你需要在 onAuthStateChanged 添加一个观察者来检测初始状态和所有后续的状态变化,

firebase.auth().onAuthStateChanged(function(user) {
  if (user) {
    // User is signed in.
  } else {
    // No user is signed in.
  }
});
这是为了获得有关身份验证状态更改的通知,它没有为我提供在任何给定时间检查用户是否为身份验证的方法
2021-04-27 15:07:42
我仍然觉得奇怪如何在第一个 console.log 对象与当前用户密钥一起存在,而在第二个 console.log 上您无法访问该密钥。似乎在第一次执行 console.log 时,该对象对浏览器可用,那么为什么不允许从该对象中选择任何内容呢?
2021-04-30 15:07:42
@Chris 请参阅下面的答案。它提供了一个示例,说明如何以适合您的要求的方式将 google firebase 与 vuex 和 vuex-persistedstate 结合使用。
2021-05-03 15:07:42
const user = firebase.auth().currentUser; if (user) { // 做一些事情 return; } // 有时在获取已认证用户时会出现延迟,因此我们必须等待 auth 状态更改才能触发 firebase.auth().onAuthStateChanged(function (user) { unsubscribe(); // 取消订阅所以我们不会永远观察 if (user) { // 做一些事情 } else { alert('你没有登录') } });
2021-05-10 15:07:42
实际上,当确定初始状态时,这将始终触发。之后它将在身份验证状态更改时触发。这是检测初始身份验证状态的推荐方法。您可以在确定初始状态后将其删除。var unsubscribe = auth1.onAuthStateChanged(function(user) { // 执行初始化操作然后取消订阅;unsubscribe();});
2021-05-11 15:07:42

始终访问 currentUser 的最佳方法是使用 vuex 和vuex-persistedstate

//Configure firebase
firebase.initializeApp(firebaseConfig);
//When ever the user authentication state changes write the user to vuex.
firebase.auth().onAuthStateChanged((user) =>{
    if(user){
        store.dispatch('setUser', user);
    }else{
        store.dispatch('setUser', null);
    }
});

上面唯一的问题是,如果用户在浏览器上按下刷新,vuex 状态将被丢弃,您必须等待 onAuthStateChange 再次触发,因此当您尝试访问 currentUser 时为什么会得到 null。

上述代码一直工作的秘诀是使用 vuex 持久化状态。

在您的 store.js 文件中

import Vue from 'vue'
import Vuex from 'vuex'
import firebase from 'firebase/app'
Vue.use(Vuex)
import createPersistedState from "vuex-persistedstate";
export default new Vuex.Store({
    plugins: [createPersistedState()],
  state: {
    user: null
  },
    getters:{
      getUser: state => {
          return state.user;
      }
    },
  mutations: {
    setUser(state, user){
      state.user = user;
    }
  },
  actions: {
    setUser(context, user){
        context.commit('setUser', user);
    },
    signIn(){
        let provider = new firebase.auth.GoogleAuthProvider();
        firebase.auth().signInWithPopup(provider).then(function (result) {
      })
    },
    signOut(){
        firebase.auth().signOut();
    }
  }
})

您现在可以保护路由器中的路由,如下面的代码示例所示。

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import Search from '@/components/Search/Search'
import CreateFishingSite from '@/components/FishingSites/CreateFishingSite'
Vue.use(Router);
import store from './store'
import firebase from 'firebase'

let router = new Router({
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
      {
          path: '/search/:type',
          name: 'Search',
          component: Search
      },
      {
          path: '/fishingsite/create',
          name: 'CreateFishingSite',
          component: CreateFishingSite,
          meta: {
              requiresAuth: true
          }
      }

  ]
})

router.beforeEach(async (to, from, next)=>{
    let currentUser = store.state.user;
    console.log(currentUser);
    let requriesAuth = to.matched.some(record => record.meta.requiresAuth);
    if(requriesAuth && !currentUser){
        await store.dispatch('signIn');
        next('/')
    }else{
        next()
    }
})
我在重新加载时使用 onAuthStateChanged 在 vuex 商店中再次获取和设置令牌和用户。然后我实现了 vuex-persistedstate。我现在有一个竞争条件,其中 onAuthStateChanged 在 vuex-persistedstate 之前触发。我不想完全禁用 onAuthStateChanged 中的调用,因为它们也在登录和注册期间使用,而不仅仅是重新加载。实现这一点的正确方法是什么?
2021-04-19 15:07:42

一个简单的方法是添加一个挂起状态。

这是一个使用钩子的react示例:

// 使用Auth.ts

import { useState, useEffect } from 'react'
import { auth } from 'firebase'

export function useAuth() {
  const [authState, setAuthState] = useState({
    isSignedIn: false,
    pending: true,
    user: null,
  })

  useEffect(() => {
    const unregisterAuthObserver = auth().onAuthStateChanged(user =>
      setAuthState({ user, pending: false, isSignedIn: !!user })
    )
    return () => unregisterAuthObserver()
  }, [])

  return { auth, ...authState }
}

// 登录.tsx

import React from 'react'
import { StyledFirebaseAuth } from 'react-firebaseui'
import { useAuth } from '../hooks'

export default function SignIn() {
  const { pending, isSignedIn, user, auth } = useAuth()

  const uiConfig = {
    signInFlow: 'popup',
    signInOptions: [
      auth.GoogleAuthProvider.PROVIDER_ID,
      auth.FacebookAuthProvider.PROVIDER_ID,
    ],
  }

  if (pending) {
    return <h1>waiting...</h1>
  }

  if (!isSignedIn) {
    return (
      <div>
        <h1>My App</h1>
        <p>Please sign-in:</p>
        <StyledFirebaseAuth uiConfig={uiConfig} firebaseAuth={auth()} />
      </div>
    )
  }

  return (
    <div>
      <h1>My App</h1>
      <p>Welcome {user.displayName}! You are now signed-in!</p>
      <a onClick={() => auth().signOut()}>Sign-out</a>
    </div>
  )
}

如果您正在寻找复制和粘贴 Auth 路由以与 firebase react:

const AuthRoute = ({ component: Component, ...rest }) => {
      const [authenticated, setAuthenticated] = useState(false)
      const [loadingAuth, setLoadingAuth] = useState(true)

      useEffect(() => {
        firebase.auth().onAuthStateChanged((user) => {
          if (user) {
            setAuthenticated(true)
          } else {
            setAuthenticated(false)
          }
          setLoadingAuth(false)
        })
      }, [])
      return loadingAuth ? 'loading...' : (
        <Route
          {...rest}
          render={props =>
            authenticated ? (
              <Component {...props} />
            ) : (
              <Redirect to={{ pathname: '/user/login' }} />
            )}
        />

      )
    }
  // On component load.
  componentDidMount = () => this.getAuthStatus();

  // Get firebase auth status.
  getAuthStatus = () => {
    firebase.auth().onAuthStateChanged((resp) => {

        // Pass response to a call back func to update state
        this.updateUserState(resp);
    });
  }

  // update state
  updateUserState = (resp) => {
     this.setState({
         user: resp
     })
  }

  // Now you can validate anywhere within the component status of a user
  if (this.state.user) { /*logged in*/}