FirestoreでauthUserに関連するユーザーデータベースの詳細を取得するにはどうすればよいですか?


10

私は、Firebase Authenticationモデルによって作成された属性とマージされた、ユーザーコレクションに格納されている属性であるユーザー名を取得する方法を理解しようとしています。

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属性を取得するために機能する)から、Authenticationコレクションから同じuidのIDを持つユーザーコレクションに取得する方法が見つかりません。

私はこの投稿を見て、同じ問題があるようであり、答えが示唆しているはずのものを解決しようとしました-しかし、Authenticationコレクションからユーザーコレクションに到達するために機能する方法を見つけることができないようです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のプロパティ 'email'を読み取れません

更新: コンソールログで、ラベルが付いたドロップダウンメニューを展開する必要があることに気づきました。

Q {I:配列(0)、l:

email属性を確認します。この意味不明な表現が何を示唆しているのか誰か知っていますか?Q、I、またはlの意味を理解するためのキーが見つからないため、認証テーブルの関連する属性にアクセスするためにこれらを参照する必要があるかどうかを確認できません。多分それを理解することができれば-Authenticationコレクションのuidを使用してユーザーコレクションにアクセスする方法を見つけることができます。

現在のユーザーが誰であるかを確認するためにコンテキストコンシューマーを使用して、誰かがフロントエンドで反応を使用しましたか?その場合、認証モデルの属性にどのようにアクセスし、関連するユーザーコレクションの属性にどのようにアクセスしましたか(ユーザードキュメントのdocIdは認証テーブルのuidです)。

次の試み

次の試みは非常に奇妙な結果を生みました。

コンテキストコンシューマである2つの個別のページがあります。それらの違いは、1つは関数であり、もう1つはクラスコンポーネントであるということです。

関数コンポーネントでは、{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;

次の試み

この試みで、データベースに存在することが確認できる値をコンソールログに記録しようとしていて、名前の値がdbに文字列が含まれている「undefined」として返されているのは本当に奇妙です。

この試みには、

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

私は言うエラーが出ます:

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);

次の試み

次に、ダッシュボードのルートをAuthContext.Consumer内にラップして、コンポーネント全体で使用できるようにしました。これにより、componentDidMount関数でログインしているユーザーにアクセスできるようになりました。

ルートを次のように変更しました:

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

ダッシュボードコンポーネントのrenderステートメントからコンシューマーを削除しました。

次に、ダッシュボードコンポーネントの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");
      }

    );  
  }

多分そのステップを解決することは、これを結果に向けて動かす手助けとなる手がかりです。ログインしているユーザーリスナーのuidを使用してユーザーコレクションレコードを取得する方法を示す、より優れたFirestoreのドキュメントを見つけられる人はいますか?

そのため、FriendlyEatsコードラボのから、コード内のid検索値にdoc.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 Authではユーザーのみが存在し、「認証テーブル」はありません。Firestoreには、コレクションとそれらのコレクション内のドキュメントがありますが、テーブルはありません。私はあなたがどこに行き詰まっているのか、そしてあなたが示したコードがあなたが期待するように機能しないのかを理解しようとしていますが、私はそれをつなぎ合わせているだけではありません。ドキュメントを見つけるより標準的な用語を使用するように質問を編集することを検討し、期待どおりに機能していないものをより明確にします。
ダグスティーブンソン

ファイン-コレクションの代わりにテーブルを使用できます。ポイントは同じです。
メル

私の主なポイントは、私はあなたのポイントを本当に得ることができなかったということです、そして、専門用語は役に立ちませんでした。あなたが示したコードで何がうまくいかないのか、もっと詳しく説明してもらえますか?期待どおりに動作しませんでしたか?説明するエラーまたはデバッグログはありますか?
ダグスティーブンソン

何も機能しません。authUserリスナーからユーザーコレクションの詳細にアクセスする方法を見つけることを期待しています。authUserは、コンテキストハンドラーと、メソッドの変更をリッスンする関連クラスメソッドで定義されます。認証コレクションの属性を通り抜けることができません-Firestoreの関連するユーザーコレクションにアクセスしようとしています。ログはありません。フィールドが未定義であるというエラーメッセージだけです。
メル

1
簡単なタスクから始めて、いくつかの経験を得るためにそれを機能させて、それを現在のようなより複雑な問題に適用することをお勧めします。ドキュメンテーションについて根本的に不正確なものは何もありません(私はそれを常に使用しているので知っています、そして私は同じで人々を助けます)。Stack Overflowの助けを借りるために、特定の問題、理想的には誰でも問題を再現するために使用できるMCVEを説明する必要があります。「うまく動かない」とだけでは不十分です。stackoverflow.com/help/minimal-reproducible-example
Doug Stevenson

回答:


5

質問の最後の行(users = () => this.db.collection('users');)から、ユーザーに関する追加情報を保存するコレクションが呼び出さusersれ、このコレクション内のユーザードキュメントがdocIdとしてuserId(uid)を使用していることがわかりました。

以下はそのトリックを実行する必要があります(テストされていません):

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内のこのユーザーに対応する一意のドキュメントをクエリするために使用しますusers1つのドキュメントドキュメントのドキュメントを参照)get()方法)。


それで、onAuthUserListenerを定義した方法に何か問題がありますか?次に、そのメソッドの修正を試した場合、authUserからユーザーコレクションにアクセスするにはどうすればよいですか?
メル

「それで、私がonAuthUserListenerを定義した方法に何か問題がありますか?」->私が見ることができるものからではありません。「authUserからユーザーコレクションにアクセスするにはどうすればよいですか?」->私が正しく理解していれば、コレクションではなく1つのドキュメントを取得したいと考えています。回答のコードは機能するはずです。
Renaud Tarnec

authUserを取得したい-あなたのコードは私の試みの改善ですか?同じuidを持つユーザーコレクションへのアクセス権をauthUserに与える方法が見つかりません。私はあなたのコード提案を理解しようとしています-それが最初のステップとして私の上でどのように改善するかについて。どの部分が改善/修正であるかを特定できますか?ありがとう
Mel

するとどうなりますかthis.auth.onAuthStateChanged(authUser => { if (authUser) {console.log(authUser.uid) })
Renaud Tarnec

すべてのauthUserプロパティ(認証収集データ)を出力できます。idがuidのユーザーコレクションの関連ドキュメントからユーザーデータにアクセスできません
Mel

1

私はあなたにテストして欲しいという理論を持っています。

ハンドラーnext(authUser)内で呼び出すonAuthStateChangedと、実行中にエラー(などcannot read property 'name' of undefined at ...)が発生すると思います。

コードが期待どおりに機能しないのは、を呼び出す場所がPromiseチェーンのnext(authUser)内部にthen()あるためです。Promise内でスローされたエラーはキャッチされ、Promiseが拒否されます。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(() => 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メソッドがからのサブスクライブ解除関数を返すことに注意してください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();
  }
}

ありがとうございました!今夜食べます。どちらの方法でもこれについて学ぶのを楽しみにしています。まもなくフィードバックが届きます。
Mel

こんにちはサム-この提案を提供してくれてありがとう 私はあなたがリンクしたドキュメントを読むのに少し時間をかけました、そして私はこれについていくつか行ってきました。私は助けに感謝しています-これは私の問題を解決しませんでした。ユーザーコレクション属性にアクセスしようとすると、次のエラーが引き続き表示されます:TypeError:未定義のプロパティ 'user'を読み取れません
Mel

@Mel元のコードを実行したときに、TypeErrorが通知されましたか?あなたが言及したのはこれが初めてです。つまり、このコードは、Promiseのスコープ外でエラーをスローするという仕事をしました。の出力を提供できますconsole.log(snapshot.data())か?
samthecodingman

私はこれを試しました-エラーメッセージは言う:TypeError:snapshot.data is not a function
Mel

私はそれを動かし続けるつもりです-これを良い場所に記録しようとしているのではないかもしれません
Mel

0

あなたには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オブジェクトがパブリック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よりも多いので、認証テーブルのプロパティを読み取ります
Mel

リスナーのcomponentDidMount関数に対する提案された変更はエラーをスローしませんでしたが、機能しませんでした。このリスナーを使用してダッシュボードコンポーネントのauthUserの値をログに記録しようとすると、authUserが定義されていないというエラーが表示されます。AuthContext.Providerを定義した方法でcomponentDidMountを使用すると、このエラーは発生しません。ログメッセージのランダムプロパティに関する情報をお寄せいただきありがとうございます。
メル

@Mel Dashboardファイルの最終行がexport default withAuthentication(Dashboard);(ではなくwithFirebase)であることを確認できますか
samthecodingman

確認しました。withFirebaseはwithAuthenticationに組み込まれているため、そのHOCを通じて取得されます。
メル

@メルSlackで送信したメッセージを確認できますか
samthecodingman

0

他の誰かが同様に行き詰まっている場合、私はここで解決策を見つけました:Firebase&React:CollectionReference.doc()argument type

ページの更新では機能しません(それでもuidがnullであるというエラーがスローされます)が、useEffectへの反応フックは、componentDidMount関数をMountとUpdateの組み合わせで置き換える必要があります。私は次にそれを試みます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.