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


10

我试图弄清楚如何获取用户名,该用户名是存储在用户集中的属性,该用户集中已与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中-我认为我尝试将Authentication模型中的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相同。

我看过这篇文章,似乎也有同样的问题,并试图找出答案应该暗示的内容-但我似乎找不到找到一种方法来从Authentication集合传递到user集合而且我不知道合并对我有什么作用,如果合并不能使我从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,email等的详细信息,但是它位于一长串的项目中-其中许多以1或2个字母开头(我找不到找到每个前缀的键字母意思)。示例摘录如下:

在此处输入图片说明

此评论的更新:

之前,我说过:uid,email等字段似乎未嵌套在这些前缀下,但是如果我尝试执行以下操作:

 console.log(authUser.email)

,我看到一条错误消息:

TypeError:无法读取null的属性“ email”

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

Q {I:Array(0),l:

查看电子邮件属性。有人知道这个低调的暗示吗?我找不到找出Q,I或l含义的密钥,以了解是否应该参考这些内容以获取Authentication表中的相关属性。也许我能弄清楚-我可以找到一种使用Authentication集合中的uid进入用户集合的方法。

有没有人在前端与上下文使用者互动,以找出当前用户是谁?如果是这样,您如何访问它们在身份验证模型上的属性以及如何访问相关的用户集合的属性(其中,用户文档上的docId是身份验证表中的uid)?

下次尝试

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

我有2个单独的页面是上下文使用者。它们之间的区别在于,一个是函数,另一个是类组件。

在功能组件中,我可以呈现{authUser.email}。当我尝试在类组件中执行相同的操作时,出现错误消息:

TypeError:无法读取null的属性“ email”

此错误来自与同一登录用户相同的会话。

注意:虽然Firebase文档说auth上有currentUser属性可用,但我根本无法使它正常工作。

我的功能组件有:

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相同的User集合属性,但可以从此组件中输出该用户在auth集合上的email属性。

尽管Firebase文档在此处提供了有关管理用户和访问属性的建议,但我还没有找到在响应中实现此方法的方法。尝试进行此尝试的每一种变体,包括在我的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

                }

我收到一条错误消息:

TypeError: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>
              })
            } 

我收到一条错误消息:

Line 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>
              })
            }

错误消息显示:

需要一个赋值或函数调用,而是看到一个表达式

我准备放弃尝试查找如何使快照查询正常工作的方法-如果我可以获取要在屏幕上呈现的用户集合的名称。有人可以帮忙吗?

下次尝试

我找到了这个帖子。它很好地解释了需要发生什么,但是我不能实现如图所示,因为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);

下次尝试

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

我将路线更改为:

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

并将使用者从仪表板组件的render语句中删除。

然后在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代码实验室示例中看到,尝试将doc.id赋予代码中的id搜索值。我不知道此代码是用什么语言编写的-但它确实与Im试图执行的操作类似-我只是看不到如何从该示例过渡到我知道如何使用的语言。

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

仅供参考,您的用语不太正确,这使这个问题难以阅读。Firebase中没有所谓的“表”。在Firebase身份验证中,只有用户-没有“身份验证表”。在Firestore中,有集合,这些集合中有文档,但是没有表。我试图弄清楚您在哪里陷入困境,以及所显示的代码如何无法按您期望的方式工作,但我只是没有将它们拼凑在一起。考虑编辑问题以使用您会找到文档的更多标准术语,并更清楚地了解哪些功能不符合您的期望。
道格·史蒂文森

很好-很高兴用表格代替收藏品。观点还是一样。
梅尔

我的主要观点是,我无法真正理解您的意思,并且该术语也无济于事。您能否更详细地说明所显示的代码中什么无效?某些东西没有按预期工作吗?有任何错误或调试日志可以说明吗?
道格·史蒂文森

没用。我期望找到一种从authUser侦听器访问用户集合详细信息的方法。authUser在上下文处理程序和相关的类方法中定义,该方法侦听方法的更改。我无法通过身份验证集合中的属性-我试图访问Firestore中的相关用户集合。没有日志。仅显示该字段未定义的错误消息。
梅尔

1
我建议从一个简单的任务开始,让它开始工作以获取一些经验,然后将其应用于更复杂的问题,例如您现在遇到的问题。该文档在本质上没有什么不对的(我知道,因为我一直都在使用它,并且我会帮助那些人)。为了获得有关Stack Overflow的帮助,您需要说明一个特定的问题,最好是任何人都可以用来重现该问题的MCVE。仅仅说“我无法使它工作”是不够的。stackoverflow.com/help/minimal-reproducible-example
Doug Stevenson

Answers:


5

我从问题(users = () => 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集合中与该用户相对应的唯一文档(请参阅阅读一个文档,以及该文档的doc get()方法)。


那么我定义onAuthUserListener的方式有问题吗?然后,如果我尝试对该方法进行修改,那么我应该怎么做才能从authUser获取用户集合?
梅尔

“那么我定义onAuthUserListener的方式有问题吗?”->从我所看到的来看不是。“我应该怎么做才能从authUser获取用户集合?” ->如果我理解正确,那么您想获取一个文档,而不是集合文档。答案中的代码应该起作用。
Renaud Tarnec

我想获得authUser-您的代码是否对我的尝试有所改进?我找不到能使authUser授予我访问具有相同uid的用户集合的方法。我正在尝试了解您的代码建议-作为第一步如何改进我的建议。请您确定其中哪一点是改进/校正?谢谢
Mel Mel

如果这样做会this.auth.onAuthStateChanged(authUser => { if (authUser) {console.log(authUser.uid) })怎样?
Renaud Tarnec

我可以输出所有authUser属性(身份验证收集数据)。我无法从以uid为id的用户集合中的相关文档中获取用户数据
Mel

1

我有一个理论,我想请您检验。

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

您的代码无法按预期运行的原因是因为您在处调用next(authUser),它then()位于Promise链的内部。承诺中引发的任何错误都将被捕获并导致承诺被拒绝。当Promise被拒绝时,它将调用带有错误的附加错误处理程序。有问题的Promise链当前没有任何这样的错误处理程序。

如果我迷路了,请阅读此博客文章,了解Promises速成课程,然后再回来。

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

因此,在您的代码中,您将替换

next(authUser)

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

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

重要的是,您没有可以在userDocRef.get()失败时进行处理的catch处理程序。因此,只需.catch(() => setTimeout(fallback))在末尾添加,then()以便您的代码在出错时使用后备方法。

因此,我们最终得到:

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方法从中返回unsubscribe函数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();
  }
}

谢谢!今晚我会尝试的。我很高兴了解这两种方式。我很快会返回反馈。
梅尔

嗨,山姆-谢谢您提出这个建议。我花了一些时间仔细阅读了您链接的文档,并为此做了一些努力。虽然我很感谢您的帮助,但这并不能解决我的问题。当我尝试访问用户集合属性时,仍然收到错误消息:TypeError:无法读取未定义的属性“ user”
Mel

@Mel运行原始代码时,是否收到TypeError通知?这是您第一次提到它。这意味着该代码确实完成了将错误抛出Promise范围之外的工作。你能提供输出console.log(snapshot.data())吗?
samthecodingman

我试过了-错误消息说:TypeError:snapshot.data不是函数
Mel

我会继续尝试移动它-也许我不是想在一个好的位置登录它
梅尔

0

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

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

应该将其更改为使用onAuthUserListenerhelper类中的:

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

关于填充有许多随机属性的日志消息,这是因为该firebase.User对象既具有公共API,又具有带有许多私有属性的实现,并且在编译时将其最小化。由于这些缩小的属性和方法未明确标记为不可枚举,因此它们包含在任何日志输出中。相反,如果您只想记录实际有用的部分,则可以使用以下方法来解构和重组对象:

// 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还要多-因此,我不打算读取身份验证表的属性
梅尔

建议对侦听器componentDidMount函数进行的更改未引发错误-但却不起作用。当它尝试使用此侦听器在仪表板组件中记录authUser的值时-我收到一条错误消息,提示authUser未定义。以我定义的方式将ComponentDidMount用于AuthContext.Provider时,没有出现此错误。感谢您提供有关日志消息中随机属性的信息。
梅尔

@Mel您是否可以确认Dashboard文件的最后一行是export default withAuthentication(Dashboard);(而不是withFirebase
samthecodingman

已确认。withFirebase包含在withAuthentication中-因此它是通过该HOC获取的。
梅尔

你@Mel可以检查我给你发了松弛的消息
samthecodingman

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.