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

Mel*_*Mel 10 firebase reactjs firebase-authentication google-cloud-firestore

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

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

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

import React from 'react';
const AuthUserContext = React.createContext(null);
export default AuthUserContext;
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

在我的 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();
      }
    });
Run Code Online (Sandbox Code Playgroud)

我找不到从 authUser(它可以让我进入 Authentication 属性)获取的方法 - 到具有与 Authentication 集合中相同 uid 的 id 的用户集合。

我看过这篇文章,它似乎有同样的问题,并试图找出答案应该暗示什么 - 但我似乎无法找到一种从 Authentication 集合到用户集合的方法如果合并不能让我访问来自 authUser 的用户集合上的属性,我不知道合并对我做了什么。

我试图在我的 firebase.js 中使用一个助手从 uid 给我一个用户 - 但这似乎也没有帮助。

user = uid => this.db.doc(`users/${uid}`);
  users = () => this.db.collection('users');
Run Code Online (Sandbox Code Playgroud)

下次尝试

为了添加更多背景,我制作了一个可以记录(但不能渲染)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;
Run Code Online (Sandbox Code Playgroud)

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

在此处输入图片说明

更新此评论:

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

 console.log(authUser.email)
Run Code Online (Sandbox Code Playgroud)

,我收到一条错误消息:

类型错误:无法读取 null 的属性“电子邮件”

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

Q {I: 数组(0), l:

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

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

下一次尝试

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

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

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

类型错误:无法读取 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;
Run Code Online (Sandbox Code Playgroud)

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

虽然Firebase 文档在这里提供了有关管理用户和访问属性的建议,但我还没有找到在 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;
Run Code Online (Sandbox Code Playgroud)

在我的 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;
Run Code Online (Sandbox Code Playgroud)

下一次尝试

很奇怪,通过这次尝试,我试图控制台记录我可以看到存在于数据库中的值,并且 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);
Run Code Online (Sandbox Code Playgroud)

下一次尝试

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

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

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

    } 
Run Code Online (Sandbox Code Playgroud)

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

你实际上如何打印输出?

当我尝试:

{ 


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

                }
Run Code Online (Sandbox Code Playgroud)

我收到一条错误消息:

类型错误: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>
              })
            } 
Run Code Online (Sandbox Code Playgroud)

我收到一条错误消息:

第 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>
              })
            }
Run Code Online (Sandbox Code Playgroud)

错误消息说:

期望赋值或函数调用,而是看到了一个表达式

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

下一次尝试

我找到了这个帖子。它对需要发生的事情有一个很好的解释,但我无法实现它,因为 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);
Run Code Online (Sandbox Code Playgroud)

下一次尝试

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

我把路线改为:

<Route path={ROUTES.DASHBOARD} render={props => (
          <AuthUserContext.Consumer>
             { authUser => ( 
                <Dashboard authUser={authUser} {...props} />  
             )}
          </AuthUserContext.Consumer>
        )} />
Run Code Online (Sandbox Code Playgroud)

并从仪表板组件渲染语句中删除了使用者。

然后在 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,
      });  
  }                  
Run Code Online (Sandbox Code Playgroud)

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

FirebaseError:Function 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!");
      }
Run Code Online (Sandbox Code Playgroud)

与我在 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");
      }

    );  
  }
Run Code Online (Sandbox Code Playgroud)

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

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

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

Ren*_*nec 6

我从您的问题 ( 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();
      }
    });
Run Code Online (Sandbox Code Playgroud)

因此,在通过该onAuthStateChanged()方法设置的观察者中,当我们检测到用户已登录(即 in if (authUser) {})时,我们使用它uid来查询users集合中与该用户对应的唯一文档(参见阅读一个文档,以及get()方法)。


Mel*_*Mel 0

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

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