您如何获取与 Firestore 中的 authUser 相关的用户数据库详细信息?

Posted

技术标签:

【中文标题】您如何获取与 Firestore 中的 authUser 相关的用户数据库详细信息?【英文标题】:How do you get the user db details related to an authUser in Firestore? 【发布时间】:2020-04-26 16:15:14 【问题描述】:

我试图弄清楚如何获取用户名,该用户名是存储在用户集合中的属性,该属性已与 firebase 身份验证模型创建的属性合并。

我可以访问 authUser - 这为我提供了 firebase 在身份验证工具中收集的有限字段,然后我尝试从那里访问相关的用户集合(使用相同的 uid)。

我有一个反应上下文消费者:

import React from 'react';
const AuthUserContext = React.createContext(null);
export default AuthUserContext;

然后在我尝试使用的组件中:

const Test = () => (

<AuthUserContext.Consumer>
    authUser => (

    <div>
            authUser.email // I can access the attributes in the authentication collection 
            authUser.uid.user.name //i cannot find a way to get the details in the related user collection document - where the uid on the collection is the same as the uid on the authentication table


     </div>
    )
</AuthUserContext.Consumer>
);

const condition = authUser => !!authUser;
export default compose(
withEmailVerification,
withAuthorization(condition),
)(Test);

在我的 firebase.js 中 - 我想我已经尝试将身份验证模型中的 authUser 属性与用户集合属性合并,如下所示:

class Firebase 
  constructor() 
    app.initializeApp(config).firestore();
    /* helpers */
    this.fieldValue = app.firestore.FieldValue;


    /* Firebase APIs */
    this.auth = app.auth();
    this.db = app.firestore();

onAuthUserListener = (next, fallback) =>
    this.auth.onAuthStateChanged(authUser => 
      if (authUser) 
        this.user(authUser.uid)
          .get()
          .then(snapshot => 
            const dbUser = snapshot.data();
            // default empty roles
            if (!dbUser.roles) 
              dbUser.roles = ;
            
            // merge auth and db user
            authUser = 
              uid: authUser.uid,
              email: authUser.email,
              emailVerified: authUser.emailVerified,
              providerData: authUser.providerData,
              ...dbUser,
            ;
            next(authUser);
          );
       else 
        fallback();
      
    );

我找不到从 authUser(它可以让我使用 Authentication 属性)获取到用户集合的方法,该集合的 id 与 Authentication 集合中的 uid 相同。

我见过this post,它似乎有同样的问题,并试图找出答案应该暗示的内容 - 但我似乎无法找到一种可以从身份验证集合中获取的方法到用户集合,如果合并没有让我从 authUser 访问用户集合上的属性,我不知道合并对我做了什么。

我尝试在我的 firebase.js 中使用帮助程序来为我提供来自 uid 的用户 - 但这似乎也没有帮助。

user = uid => this.db.doc(`users/$uid`);
  users = () => this.db.collection('users');

下一次尝试

为了添加更多背景,我做了一个可以记录(但不能渲染)authUser的测试组件,如下:

import React,  Component  from 'react';
import  withFirebase  from '../Firebase/Index';
import  Button, Layout   from 'antd';

import  AuthUserContext, withAuthorization, withEmailVerification  from '../Session/Index';


class Test extends Component 
  constructor(props) 
    super(props);

    this.state = 
      loading: false,
      user: null,
      ...props.location.state,
    ;
  

  componentDidMount() 
    if (this.state.user) 
      return;
    

    this.setState( loading: true );

    // this.unsubscribe = this.props.firebase
    //   .user(authUser.uid)
    //   .onSnapshot(snapshot => 
    //     const userData = snapshot.data();  
    //     console.log(userData);
    //     this.setState(
    //       user: snapshot.data(),
    //       loading: false,
    //     );
    //   );
  

  componentWillUnmount() 
    this.unsubscribe && this.unsubscribe();
  



  render() 
    const  user, loading  = this.state;


    return (
        <div>
        <AuthUserContext.Consumer>
        authUser => (
            console.log(authUser),
            <p>
                </p>


            )
            </AuthUserContext.Consumer> 

        </div>

    );

    ;

export default Test;

日志在日志中显示了 uid、电子邮件等的详细信息,但它在一长串项目中 - 其中许多以 1 或 2 个字母开头(我找不到找出每个项目的关键这些前缀字母的意思)。下面提取的示例:

对此评论的更新:

之前,我说过: uid、email 等字段似乎没有嵌套在这些前缀下方,但如果我尝试:

 console.log(authUser.email)

,我收到一条错误消息:

TypeError: 无法读取 null 的属性“电子邮件”

更新: 我刚刚意识到,在控制台日志中,我必须展开一个标记为的下拉菜单:

Q I: Array(0), l:

查看电子邮件属性。有谁知道这个乱七八糟的暗示是什么?我找不到密钥来弄清楚 Q、I 或 l 的含义,以了解我是否应该引用这些东西来获取 Authentication 表中的相关属性。也许如果我能弄清楚 - 我可以找到一种使用身份验证集合中的 uid 访问用户集合的方法。

有没有人在前端使用 react,通过上下文消费者来找出当前用户是谁?如果是这样,您如何访问它们在 Authentication 模型上的属性以及您如何访问相关 User 集合上的属性(其中 User 文档上的 docId 是 Authentication 表中的 uid)?

下一次尝试

下一次尝试产生了一个非常奇怪的结果。

我有 2 个单独的页面作为上下文消费者。它们的区别在于一个是函数,另一个是类组件。

在函数组件中,我可以渲染 authUser.email。当我尝试在类组件中做同样的事情时,我收到一条错误消息:

TypeError: 无法读取 null 的属性“电子邮件”

这个错误来自同一个登录用户的同一个会话。

注意:虽然 firebase 文档说 currentUser 属性在 auth 上可用 - 我根本无法让它工作。

我的功能组件有:

import React from 'react';
import  Link  from 'react-router-dom';
import  compose  from 'recompose';
import  AuthUserContext, withAuthorization, withEmailVerification  from '../Session/Index';


const Account = () => (

<AuthUserContext.Consumer>
    authUser => (
    <div>
         authUser.email
    </div>
    )
</AuthUserContext.Consumer>
);

// const condition = authUser => !!authUser;
// export default compose(
// withEmailVerification,
// withAuthorization(condition),
// )(Account);
export default Account;

虽然我无法访问用户文档上的 docId 与经过身份验证的用户的 uid 相同的用户集合属性,但从这个组件中,我可以输出该用户的身份验证集合上的电子邮件属性。

虽然Firebase documentation 在这里提供了管理用户和访问属性的建议,但我还没有找到在 react 中实现这种方法的方法。通过在我的 firebase.js 中创建帮助程序以及尝试在组件中从头开始尝试执行此操作的每一种变化都会在访问 firebase 时产生错误。但是,我可以生成用户列表及其相关的用户集合信息(我无法根据 authUser 的身份获取用户)。

我的班级组件有:

import React from 'react';
import 
    BrowserRouter as Router,
    Route,
    Link,
    Switch,

   from 'react-router-dom';
import * as ROUTES from '../../constants/Routes';
import  compose  from 'recompose';
import  withFirebase  from '../Firebase/Index';
import  AuthUserContext, withAuthorization, withEmailVerification  from '../Session/Index';



class Dashboard extends React.Component 
  state = 
    collapsed: false,
  ;

  onCollapse = collapsed => 
    console.log(collapsed);
    this.setState( collapsed );
  ;

  render() 
    const   loading  = this.state;
    // const dbUser = this.props.firebase.app.snapshot.data();
    // const user = Firebase.auth().currentUser;
    return (
    <AuthUserContext.Consumer>
      authUser => (  

        <div>    
         authUser.email // error message as shown above
          console.log(authUser) // output logged in amongst a long list of menus prefixed with either 1 or 2 characters. I can't find a key to decipher what these menus mean or do.
        </div>
      )
    </AuthUserContext.Consumer>  
    );
  


//export default withFirebase(Dashboard);
export default Dashboard;

在我的 AuthContext.Provider - 我有:

import React from 'react';
import  AuthUserContext  from '../Session/Index';
import  withFirebase  from '../Firebase/Index';
const withAuthentication = Component => 
  class WithAuthentication extends React.Component 
    constructor(props) 
      super(props);
      this.state = 
        authUser: null,
      ;  
    

    componentDidMount() 
      this.listener = this.props.firebase.auth.onAuthStateChanged(
        authUser => 
          authUser
            ? this.setState( authUser )
            : this.setState( authUser: null );
        ,
      );
    

    componentWillUnmount() 
      this.listener();
    ;  

    render() 
      return (
        <AuthUserContext.Provider value=this.state.authUser>
          <Component ...this.props />
        </AuthUserContext.Provider>
      );
    
  
  return withFirebase(WithAuthentication);

;
export default withAuthentication;

下一次尝试

很奇怪,通过这次尝试,我正在尝试控制台记录我可以看到数据库中存在的值,并且 name 的值作为“未定义”返回,其中 db 中有一个字符串。

此尝试:

    import React from 'react';
    import 
        BrowserRouter as Router,
        Route,
        Link,
        Switch,
        useRouteMatch,
      from 'react-router-dom';
    import * as ROUTES from '../../constants/Routes';
    import  compose  from 'recompose';
    import  withFirebase  from '../Firebase/Index';
    import  AuthUserContext, withAuthorization, withEmailVerification  from '../Session/Index';



    class Dash extends React.Component 
      // state = 
      //   collapsed: false,
      // ;

      constructor(props) 
        super(props);

        this.state = 
          collapsed: false,
          loading: false,
          user: null,
          ...props.location.state,
        ;
      
      componentDidMount() 
        if (this.state.user) 
          return;
        

        this.setState( loading: true );

        this.unsubscribe = this.props.firebase
          .user(this.props.match.params.id)
          // .user(this.props.user.uid)
          // .user(authUser.uid)
          // .user(authUser.id)
          // .user(Firebase.auth().currentUser.id)
          // .user(Firebase.auth().currentUser.uid)

          .onSnapshot(snapshot => 
            this.setState(
              user: snapshot.data(),
              loading: false,
            );
          );
      

      componentWillUnmount() 
        this.unsubscribe && this.unsubscribe();
      


      onCollapse = collapsed => 
        console.log(collapsed);
        this.setState( collapsed );
      ;

      render() 
        // const   loading  = this.state;
        const  user, loading  = this.state;
        // let match = useRouteMatch();
        // const dbUser = this.props.firebase.app.snapshot.data();
        // const user = Firebase.auth().currentUser;
        return (
        <AuthUserContext.Consumer>
          authUser => (  

            <div>    
            loading && <div>Loading ...</div>

                <Layout style= minHeight: '100vh' >
                  <Sider collapsible collapsed=this.state.collapsed onCollapse=this.onCollapse>
                    <div  />

                  </Sider>
                <Layout>

                    <Header>
                    console.log("authUser:", authUser)
                    // this log returns the big long list of outputs - the screen shot posted above is an extract. It includes the correct Authentication table (collection) attributes
                    console.log("authUser uid:", authUser.uid)
                    // this log returns the correct uid of the current logged in user
                    console.log("Current User:", this.props.firebase.auth.currentUser.uid)
// this log returns the correct uid of the current logged in user
                    console.log("current user:", this.props.firebase.db.collection("users").doc(this.props.firebase.auth.currentUser.uid
                    ))
// this log returns a big long list of things under a heading: DocumentReference _key: DocumentKey, firestore: Firestore, _firestoreClient: FirestoreClient. One of the attributes is: id: (...) (I can't click to expand this).
                    console.log("current user:", this.props.firebase.db.collection("users").doc(this.props.firebase.auth.currentUser.uid
                    ).name)
//this log returns: undefined. There is an attribute in my user document called 'name'. It has a string value on the document with the id which is the same as the currentUser.uid.
                    <Text style= float: 'right', color: "#fff">

                      user && (
                        <Text style= color: "#fff">user.name
//this just gets skipped over in the output. No error but also does not return the name.
</Text>


                      )

                    </Text>
                    </Header>      
                   </Layout>
                </Layout>

            </div>
          )
        </AuthUserContext.Consumer>  
        );
      
    

    export default withFirebase(Dash);

下一次尝试

所以这个尝试很笨拙,没有使用我上面尝试使用的帮助程序或快照查询,而是将用户集合文档属性记录到控制台,如下所示:

this.props.firebase.db.collection('users').doc(authUser.uid).get()

      .then(doc => 
          console.log(doc.data().name) 
      )

     

但我不能做的是找到一种在 jsx 中呈现该名称的方法

你是如何实际打印输出的?

当我尝试时:

 


this.props.firebase.db.collection('users').doc(authUser.uid).get().data().name

                

我收到一条错误消息:

类型错误: this.props.firebase.db.collection(...).doc(...).get(...).data 不是 功能

当我尝试时:

 



this.props.firebase.db.collection('users').doc(authUser.uid).get()
              .then(doc => 
                  console.log(doc.data().name), 
                  <p>doc.data().name</p>
              )
             

我收到一条错误消息:

第 281:23 行:应为赋值或函数调用,而不是 看到一个表达式 no-unused-expressions

当我尝试时:

 


this.props.firebase.db.collection('users').doc(authUser.uid).get("name")
              .then(doc => 
                  console.log(doc.data().name), 
                  <p>doc.data().name</p>
              )
            

错误信息说:

期待一个赋值或函数调用,却看到了一个表达式

我已经准备好放弃尝试找出如何让快照查询工作的尝试——如果我可以让用户集合的名称呈现在屏幕上。任何人都可以帮助完成这一步吗?

下一次尝试

我找到了this post。它很好地解释了需要发生的事情,但我无法如图所示实现它,因为 componentDidMount 不知道 authUser 是什么。

我目前的尝试如下 - 但是,正如目前所写,authUser 是返回值的包装器 - 而 componentDidMount 段不知道 authUser 是什么。

import React from 'react';
import 
    BrowserRouter as Router,
    Route,
    Link,
    Switch,
    useRouteMatch,
  from 'react-router-dom';
import * as ROUTES from '../../constants/Routes';
import  compose  from 'recompose';
import  Divider, Layout, Card, Tabs, Typography, Menu, Breadcrumb, Icon  from 'antd';
import  withFirebase  from '../Firebase/Index';
import  AuthUserContext, withAuthorization, withEmailVerification  from '../Session/Index';




const  Title, Text  = Typography
const  TabPane  = Tabs;
const  Header, Content, Footer, Sider  = Layout;
const  SubMenu  = Menu;


class Dashboard extends React.Component 
  // state = 
  //   collapsed: false,
  //   loading: false,
  // ;

  constructor(props) 
    super(props);

    this.state = 
      collapsed: false,
      loading: false,
      user: null,
      ...props.location.state,
    ;
  
  componentDidMount() 
    if (this.state.user) 
      return;
    

    this.setState( loading: true );

    this.unsubscribe = this.props.firebase
      .user(this.props.match.params.id)
      .onSnapshot(snapshot => 
        this.setState(
          user: snapshot.data(),
          loading: false,
        );
      );
  // 

//   firebase.firestore().collection("users")
//     .doc(this.state.uid)
//     .get()
//     .then(doc => 
//       this.setState( post_user_name: doc.data().name );
//   );
// 

  this.props.firebase.db
    .collection('users')
    .doc(authUser.uid)
    .get()
    .then(doc => 
        this.setState( user_name: doc.data().name );
        // loading: false,
      );  
                      

  componentWillUnmount() 
    this.unsubscribe && this.unsubscribe();
  


  onCollapse = collapsed => 
    console.log(collapsed);
    this.setState( collapsed );
  ;

  render() 
    // const   loading  = this.state;
    // const  user, loading  = this.state;
    // let match = useRouteMatch();
    // const dbUser = this.props.firebase.app.snapshot.data();
    // const user = Firebase.auth().currentUser;


    return (
    <AuthUserContext.Consumer>
       authUser => (  

        <div>    

                <Header>

                 /* 
                     
                    this.props.firebase.db.collection('users').doc(authUser.uid).get()
                    .then(doc => 
                        console.log( doc.data().name
)                          
                    )
                   
                  */ 


                  </Text>
                </Header>      

                      <Switch>

                      </Switch>    

        </div>
      )
    </AuthUserContext.Consumer>  
    );
  


export default withFirebase(Dashboard);

下一次尝试

接下来,我尝试将仪表板的路由包装在 AuthContext.Consumer 中,以便整个组件都可以使用它 - 从而让我在 componentDidMount 函数中访问登录用户。

我将路线改为:

<Route path=ROUTES.DASHBOARD render=props => (
          <AuthUserContext.Consumer>
              authUser => ( 
                <Dashboard authUser=authUser ...props />  
             )
          </AuthUserContext.Consumer>
        ) />

并从仪表板组件渲染语句中删除消费者。

然后在 Dashboard 组件上的 componentDidMount 中,我尝试了:

componentDidMount() 
    if (this.state.user) 
      return;
    

    this.setState( loading: true );

     this.unsubscribe =
     this.props.firebase.db
     .collection('users')
   //.doc(this.props.firebase.db.collection('users').doc(this.props.firebase.authUser.uid))
 .doc(this.props.firebase.db.collection('users').doc(this.props.authUser.uid))
     .get()
     .then(doc => 
         this.setState( name: doc.data().name );
       loading: false,
      );  
                    

当我尝试这个时,我收到一条错误消息:

FirebaseError:函数 CollectionReference.doc() 需要它的第一个 参数类型为非空字符串,但它是:自定义 DocumentReference 对象

下一次尝试 下面的人似乎在第一个提议的解决方案中找到了一些有用的东西。我无法在其中找到任何有用的东西,但是通过它的建议回读,我正在努力查看 firebase 文档中的示例(它没有披露如何为 .doc() 请求提供 :uid 值),如下:

db.collection("cities").doc("SF");

  docRef.get().then(function(doc) 
      if (doc.exists) 
          console.log("Document data:", doc.data());
       else 
          // doc.data() will be undefined in this case
          console.log("No such document!");
      

与我在 componentDidMount 函数中的尝试根本不同,即:

this.unsubscribe =
  this.props.firebase.db
    .collection('users')
    // .doc(this.props.firebase.db.collection('users').doc(this.props.firebase.authUser.uid))
    // .doc(this.props.firebase.db.collection('users').uid: this.props.firebase.auth().currentUser.uid  )
    .doc(this.props.authUser.uid)
    .get()
    .then(doc => 
        this.setState( user.name: doc.data().name );
        // loading: false,
      else 
        // doc.data() will be undefined in this case
        console.log("Can't find this record");
      

    );  
  

也许解决这一步是有助于将其推向结果的线索。谁能找到更好的 firestore 文档来展示如何使用登录的用户侦听器 uid 获取用户收集记录?

为此,我可以从 FriendlyEats 代码实验室example 看到,有人尝试将 doc.id 赋予代码中的 id 搜索值。我不知道这段代码是用什么语言编写的——但它看起来确实与我试图做的类似——我只是看不出如何从那个例子转移到我知道如何使用的东西。

display: function(doc) 
      var data = doc.data();
      data['.id'] = doc.id;
      data['go_to_restaurant'] = function() 
        that.router.navigate('/restaurants/' + doc.id);
      ;

【问题讨论】:

仅供参考,您的术语不太正确,使这个问题难以阅读。 Firebase 中没有任何东西称为“表”。在 Firebase Auth 中,只有用户 - 没有“身份验证表”。在 Firestore 中,有集合和这些集合中的文档,但没有表。我试图弄清楚你在哪里卡住了,以及你显示的代码如何没有按照你期望的方式工作,但我只是没有把它们拼凑在一起。考虑编辑问题以使用您可以在文档中找到的更标准的术语,并更清楚地了解哪些内容没有按您的预期工作。 很好 - 很乐意用表格代替集合。重点还是一样。 我的主要观点是我无法真正理解您的观点,而且术语也没有帮助。您能否更详细地解释一下您显示的代码中哪些不起作用?有什么事情没有按预期工作吗?有任何错误或调试日志来说明吗? 没有任何效果。我期待找到一种从 authUser 侦听器访问用户集合详细信息的方法。 authUser 在上下文处理程序和相关的类方法中定义,用于侦听方法的更改。我无法通过身份验证集合中的属性 - 我正在尝试访问 firestore 中的相关用户集合。没有日志。只是指出该字段未定义的错误消息。 我建议从一个简单的任务开始,让它发挥作用以获得一些经验,然后将其应用于更复杂的问题,比如你现在遇到的问题。文档没有什么根本不正确的地方(我知道,因为我一直在使用它,而且我也帮助人们)。为了在 Stack Overflow 上获得帮助,您需要说明一个特定的问题,最好是一个任何人都可以用来重现该问题的 MCVE。仅仅说“我无法让它工作”是不够的。 ***.com/help/minimal-reproducible-example 【参考方案1】:

如果其他人也遇到类似问题,我在这里找到了解决方案:Firebase & React: CollectionReference.doc() argument type

它在页面刷新时不起作用(仍然会抛出 uid 为 null 的错误),但对 useEffect 的反应挂钩应该将 componentDidMount 函数替换为 Mount 和 Update 的组合。我下一个试试。

【讨论】:

【参考方案2】:

在您的 AuthContext.Provider 实现中,您可以直接访问 SDK 的 onAuthStateChanged 侦听器:

componentDidMount() 
  this.listener = this.props.firebase.auth.onAuthStateChanged(
    authUser => 
      authUser
        ? this.setState( authUser )
        : this.setState( authUser: null );
    
  );


这应该改为使用你的助手类中的onAuthUserListener

componentDidMount() 
  this.listener = this.props.firebase.onAuthUserListener(
    /* next()     */ (authUserWithData) => this.setState(authUser: authUserWithData),
    /* fallback() */ () => this.setState(authUser: null)
  );

关于充满大量随机属性的日志消息,这是因为firebase.User 对象同时具有public API 和implementation,其中包含许多私有属性和方法,这些属性和方法在编译时会被缩小。因为这些缩小的属性和方法没有明确标记为不可枚举,所以它们包含在任何日志输出中。如果您只想记录实际有用的部分,您可以使用以下方法解构和重组对象:

// Extracts public properties of firebase.User objects
// see https://firebase.google.com/docs/reference/js/firebase.User#properties
function extractPublicProps(user) 
  let displayName, email, emailVerified, isAnonymous, metadata, phoneNumber, photoURL, providerData, providerId, refreshToken, tenantId, uid = user;
  return displayName, email, emailVerified, isAnonymous, metadata, phoneNumber, photoURL, providerData, providerId, refreshToken, tenantId, uid


function extractUsefulProps(user) 
  let displayName, email, emailVerified, isAnonymous, phoneNumber, photoURL, uid = user;
  return displayName, email, emailVerified, isAnonymous, phoneNumber, photoURL, uid


let authUser = firebase.auth().currentUser;
console.log(authUser);
console.log(extractPublicProps(authUser));
console.log(extractUsefulProps(authUser));

【讨论】:

感谢@samthecodingman 继续尝试帮助我。我看过这个。我的目标是读取 authUser 的 uid,以便我可以使用它来获取该用户的相关用户集合的属性(用户集合中的名称超过 firebase auth 集合中的 displayName - 所以我不想读取 Authentication 表的属性。 对侦听器 componentDidMount 函数的建议更改没有引发错误 - 但它不起作用。当它尝试使用此侦听器在仪表板组件中记录 authUser 的值时 - 我收到一条错误消息,提示未定义 authUser。当我以我定义的方式将 componentDidMount 用于 AuthContext.Provider 时,我没有收到此错误。感谢您提供有关日志消息中随机属性的信息。 @Mel 你能确认你的Dashboard 文件的最后一行是export default withAuthentication(Dashboard);(而不是withFirebase 已确认。 withFirebase 包含在 withAuthentication 中 - 因此它可以通过该 HOC 获取。 @Mel 你能看看我在 Slack 上发给你的消息吗【参考方案3】:

我有一个理论想让你测试一下。

我认为,当您在 onAuthStateChanged 处理程序中调用 next(authUser) 时,在执行过程中会遇到错误(例如 cannot read property 'name' of undefined at ...)。

您的代码未按预期工作的原因是因为您调用 next(authUser) 的位置位于 Promise 链的 then() 内。 Promise 中抛出的任何错误都将被捕获并导致 Promise 被拒绝。当一个 Promise 被拒绝时,它会调用它附加的错误处理程序,并带有错误。有问题的 Promise 链目前没有任何此类错误处理程序。

如果我失去了你,请阅读 this blog post 的 Promises 速成课程,然后再回来。

那么我们可以做些什么来避免这种情况呢?最简单的方法是在 Promise then() 处理程序的范围之外调用 next(authUser) outside。我们可以使用window.setTimeout(function) 来做到这一点。

所以在你的代码中,你会替换

next(authUser)

setTimeout(() => next(authUser))
// or setTimeout(() => next(authUser), 0) for the same result

这将正常抛出任何错误,而不是被 Promise 链捕获。

重要的是,当userDocRef.get() 失败时,您还没有一个 catch 处理程序来处理。所以只需在then() 的末尾添加.catch(() =&gt; setTimeout(fallback)),这样您的代码就会在出错时使用回退方法。

所以我们最终得到:

this.user(authUser.uid)
  .get()
  .then(snapshot => 
    const dbUser = snapshot.data();
    // default empty roles
    if (!dbUser.roles) 
      dbUser.roles = ;
    
    // merge auth and db user
    authUser = 
      ...dbUser, // CHANGED: Moved dbUser to beginning so it doesn't override other info
      uid: authUser.uid,
      email: authUser.email,
      emailVerified: authUser.emailVerified,
      providerData: authUser.providerData
    ;
    setTimeout(() => next(authUser), 0); // invoke callback outside of Promise
  )
  .catch((err) => setTimeout(() => fallback(), 0)); // invoke callback outside of Promise

修改后的代码

上面的解释应该可以让你修复你的代码,但这里是我的 Firebase 类版本,具有各种易于使用的变化。

用法:

import FirebaseHelper from './FirebaseHelper.js';

const fb = new FirebaseHelper();
fb.onUserDataListener(userData => 
  // do something - user is logged in!
, () => 
  // do something - user isn't logged in or an error occurred

类定义:

// granular Firebase namespace import
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';

const config =  /* firebase config goes here */ ;

export default class FirebaseHelper  // renamed from `Firebase` to prevent confusion
  constructor() 
    /* init SDK if needed */
    if (firebase.apps.length == 0)  firebase.initializeApp(config); 

    /* helpers */
    this.fieldValue = app.firestore.FieldValue;

    /* Firebase APIs */
    this.auth = firebase.auth();
    this.db = firebase.firestore();
  

  getUserDocRef(uid)  // renamed from `user`
    return this.db.doc(`users/$uid`);
  

  getUsersColRef()  // renamed from `users`
    return this.db.collection('users');
  

  /**
   * Attaches listeners to user information events.
   * @param function next - event callback that receives user data objects
   * @param function fallback - event callback that is called on errors or when user not logged in
   *
   * @returns function unsubscribe function for this listener
   */
  onUserDataListener(next, fallback) 
    return this.auth.onAuthStateChanged(authUser => 
      if (!authUser) 
        // user not logged in, call fallback handler
        fallback();
        return;
      

      this.getUserDocRef(authUser.uid).get()
        .then(snapshot => 
          let snapshotData = snapshot.data();

          let userData = 
            ...snapshotData, // snapshotData first so it doesn't override information from authUser object
            uid: authUser.uid,
            email: authUser.email,
            emailVerified: authUser.emailVerifed,
            providerData: authUser.providerData
          ;

          setTimeout(() => next(userData), 0); // escapes this Promise's error handler
        )
        .catch(err => 
          // TODO: Handle error?
          console.error('Error while getting user document -> ', err.code ? err.code + ': ' + err.message : (err.message || err));
          setTimeout(fallback, 0); // escapes this Promise's error handler
        );
    );
  

  // ... other methods ...

请注意,在此版本中,onUserDataListener 方法返回来自onAuthStateChanged 的取消订阅函数。当你的组件被卸载时,你应该分离所有相关的监听器,这样你就不会发生内存泄漏或在不需要时在后台运行损坏的代码。

class SomeComponent 
  constructor() 
    this._unsubscribe = fb.onUserDataListener(userData => 
      // do something - user is logged in!
    , () => 
      // do something - user isn't logged in or an error occurred
    ;
  

  // later
  componentWillUnmount() 
    this._unsubscribe();
  

【讨论】:

谢谢!我今晚试试这个。无论哪种方式,我都很高兴能了解这一点。我会尽快回来提供反馈。 嗨,Sam - 感谢您提供此建议。我花了一些时间阅读了您链接的文档,并且对此进行了一些尝试。虽然我很感激帮助 - 这并没有解决我的问题。当我尝试访问用户集合属性时,我仍然收到一条错误消息:TypeError: Cannot read property 'user' of undefined @Mel 当您运行原始代码时,您是否收到了 TypeError 的通知?这是你第一次提到它。这意味着这段代码完成了将错误抛出 Promise 范围之外的工作。你能提供console.log(snapshot.data())的输出吗? 我试过了 - 错误信息说:TypeError: snapshot.data is not a function 我会继续尝试移动它 - 也许我并没有尝试将它记录在一个好地方【参考方案4】:

我从您问题的最后一行 (users = () =&gt; this.db.collection('users');) 了解到,您存储有关用户的额外信息的集合称为 users,并且此集合中的用户文档使用 userId (uid) 作为 docId。

以下应该可以解决问题(未经测试):

class Firebase 
  constructor() 
    app.initializeApp(config).firestore();
    /* helpers */
    this.fieldValue = app.firestore.FieldValue;


    /* Firebase APIs */
    this.auth = app.auth();
    this.db = app.firestore();

onAuthUserListener = (next, fallback) =>
    this.auth.onAuthStateChanged(authUser => 
      if (authUser) 
           this.db.collection('users').doc(authUser.uid)
              .get()
              .then(snapshot => 
                const userData = snapshot.data();
                console.log(userData);
                //Do whatever you need with userData
                //i.e. merging it with authUser
                //......

                next(authUser);
          );
       else 
        fallback();
      
    );

所以,在通过onAuthStateChanged()方法的观察者集合内,当我们检测到用户登录时(即在if (authUser) ),我们使用它的uid来查询该用户对应的唯一文档users 集合(参见read one document,以及get() 方法的文档)。

【讨论】:

那么我定义onAuthUserListener的方式有问题吗?那么如果我尝试你对该方法的修改,我应该怎么做才能从 authUser 获取用户集合? “那么我定义 onAuthUserListener 的方式有问题吗?” -> 不是我所看到的。 “我应该怎么做才能从 authUser 获取用户集合?” -> 如果我理解正确,您想获得一份文件,而不是收藏。答案中的代码应该可以工作。 我想获得 authUser - 你的代码是对我尝试的改进吗?我找不到一种方法可以让 authUser 让我访问具有相同 uid 的用户集合。我正在尝试了解您的代码建议 - 作为第一步如何改进我的代码。请问你能确定哪一点是改进/更正吗?谢谢 如果你这样做this.auth.onAuthStateChanged(authUser =&gt; if (authUser) console.log(authUser.uid) )会发生什么? 我可以输出所有的 authUser 属性(认证集合数据)。我无法从以 uid 作为 id 的用户集合中的相关文档获取用户数据

以上是关于您如何获取与 Firestore 中的 authUser 相关的用户数据库详细信息?的主要内容,如果未能解决你的问题,请参考以下文章

Firestore:加入与 Firestore 定价

如何使用 Firestore 填充参考字段

您如何在 Firestore 中存储重复数据?

如何从flutter中的firestore文档字段中获取数据?

如何在从firestore数据库获取数据时修复状态更新

如何使用 Firestore 在 Flutter 中搜索文本