无法加载 currentUser

Can't get currentUser on load

当尝试检查用户是否通过 firebase.auth().currentUser 登录时,如下所示:

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

每当我刷新页面,或在上面导航时 returns 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() 的日志输出中看到它?

这是一个常见问题。 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.
  }
});

始终访问 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-persisted state。

在你的 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()
    }
})
firebase.auth().onAuthStateChanged(function(user) {
    if (user) {

      var user = firebase.auth().currentUser;


      if(user != null){ 
        var io=user.uid;
        window.alert("success "+io);




      }

    } else {
      // No user is signed in.
      Window.reload();

    }
  });

首先检查用户是否存在,然后通过

获取它的 ID

firebase.auth().currentUser.uid

  // 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*/}

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

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

// useAuth.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 }
}

// SignIn.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>
  )
}

如果您正在寻找用于与 firebase 反应的复制和粘贴 Auth 路由:

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' }} />
            )}
        />

      )
    }

最好的方法是使用承诺并仅在响应后实例化路由器,类似于:

store.dispatch('userModule/checkAuth').then(() => {
  // whatever code you use to first initialise your router, add it in here, for example
  new Vue({
    router,
    store,
    render: h => h(App)
  }).$mount('#app')
})

在 checkAuth 操作中是您做出承诺的地方,如下所示:

checkAuth ({ commit }) {
return new Promise((resolve, reject) => {
  firebase.auth().onAuthStateChanged(async (_user) => {
    if (_user) {
      commit('setUser', _user)
    } else {
      commit('setUser', null)
    }
    console.log('current user in checkAuth action:', _user)
    resolve(true)
  })
})

h/t 致 aaron k saunders - 我的这个解决方案的来源。

Promise-wise,共有三个选项:

// best option

const user1 = await new Promise((resolve: any, reject: any) =>
firebase.auth().onAuthStateChanged((user: any) =>
  resolve(user), (e: any) => reject(e)));

console.log(user1);

// sometimes does not display correctly when logging out

const user2 = await firebase.auth().authState.pipe(first()).toPromise();

console.log(user2);

// technically has a 3rd state of 'unknown' before login state is checked

const user3 = await firebase.auth().currentUser;

console.log(user3);

如果您希望用户只有在通过身份验证后才能访问某个页面,如果没有通过身份验证则重定向到主页,以下代码可能会有所帮助:

在反应中: 使用以下代码创建一个组件:

import { onAuthStateChanged } from "@firebase/auth";
import { Route, Redirect } from "react-router-dom";
import { auth } from "../firebase/config";
import { useState, useEffect } from "react";

const GuardedRoute = ({ component, path }) => {
  const [authenticated, setAuthenticated] = useState(false);
  const [authCompleted, setAuthCompleted] = useState(false);
  useEffect(() => {
    onAuthStateChanged(auth, (user) => {
      if (user) {
        setAuthenticated(true);
      } else {
        setAuthenticated(false);
      }
      setAuthCompleted(true);
    });
  }, []);
  return authCompleted ? (
    authenticated ? (
      <Route path={path} component={component} />
    ) : (
      <Redirect to="/" />
    )
  ) : (
    ""
  );
};
export default GuardedRoute;

并在 app.js 中使用:

import RouterPage from "./pages/RouterPage";
<GuardedRoute path="/router-page" component={RouterPage} />

在 Vue 中: 在路由器文件中使用:

const guardSuccess = (to, from, next) => {
  let gUser = auth.currentUser
  if (gUser) {
    next()
  } else {
    next({ name: "Home" })
  }
}

并在要限制访问的页面的路由中添加:

 {
    path: "/router-page",
    name: "routerPage",
    component: () => import("../views/routerPage.vue"),
    beforeEnter: guardSuccess
  }