Wie erhalten Sie die Benutzerdatenbankdaten zu einem AuthUser im Firestore?

10

Ich versuche herauszufinden, wie ein Benutzername abgerufen werden kann, bei dem es sich um ein Attribut handelt, das in einer Benutzersammlung gespeichert ist, die mit den vom Firebase-Authentifizierungsmodell erstellten Attributen zusammengeführt wurde.

Ich kann auf authUser zugreifen - das gibt mir die begrenzten Felder, die firebase im Authentifizierungstool sammelt, und dann versuche ich, von dort zur zugehörigen Benutzersammlung zu gelangen (die dieselbe UID verwendet).

Ich habe einen Reaktionskontextkonsumenten mit:

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

Dann versuche ich in meiner Komponente Folgendes zu verwenden:

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

In meiner firebase.js - Ich glaube, ich habe versucht, die authUser-Attribute aus dem Authentifizierungsmodell wie folgt mit den Benutzersammlungsattributen zusammenzuführen:

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

Ich kann keinen Weg finden, um vom authUser (der mich zu den Authentifizierungsattributen bringt) zur Benutzersammlung zu gelangen, die eine ID mit derselben UID aus der Authentifizierungssammlung hat.

Ich habe diesen Beitrag gesehen , der das gleiche Problem zu haben scheint, und versucht herauszufinden, worauf die Antwort hindeuten soll - aber ich kann anscheinend keinen Weg finden, um von der Authentifizierungssammlung zur Benutzersammlung zu gelangen und ich weiß nicht, was die Zusammenführung für mich bewirkt, wenn ich nicht über authUser auf die Attribute in der Benutzersammlung zugreifen kann.

Ich habe versucht, einen Helfer in meiner firebase.js zu verwenden, um mir einen Benutzer von einer UID zu geben - aber das scheint auch nicht zu helfen.

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

Nächster Versuch

Um mehr Hintergrundinformationen hinzuzufügen, habe ich eine Testkomponente erstellt, die authUser wie folgt protokollieren (aber nicht rendern) kann:

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;

Das Protokoll zeigt die Details für UID, E-Mail usw. in den Protokollen an, befindet sich jedoch in einer langen Liste von Elementen, von denen viele mit 1 oder 2 Buchstaben vorangestellt sind (ich kann keinen Schlüssel finden, um herauszufinden, welche dieser Präfixe jeweils vorhanden sind Buchstaben bedeuten). Beispiel unten extrahiert:

Geben Sie hier die Bildbeschreibung ein

UPDATE ZU DIESEM KOMMENTAR:

Zuvor sagte ich: Felder für UID, E-Mail usw. scheinen nicht unter diesen Präfixen verschachtelt zu sein, aber wenn ich versuche:

 console.log(authUser.email)

Ich erhalte eine Fehlermeldung, die besagt:

TypeError: Die Eigenschaft 'email' von null kann nicht gelesen werden

Das Update: Ich habe gerade festgestellt, dass ich im Konsolenprotokoll ein Dropdown-Menü mit der Bezeichnung:

Q {I: Array (0), l:

um das E-Mail-Attribut zu sehen. Weiß jemand, worauf dieser Quatsch anspielt? Ich kann keinen Schlüssel finden, um herauszufinden, was Q, ich oder ich bedeutet, um zu wissen, ob ich auf diese Dinge verweisen soll, um zu den relevanten Attributen in der Authentifizierungstabelle zu gelangen. Wenn ich das herausfinden kann, kann ich vielleicht einen Weg finden, mit der UID aus der Authentifizierungssammlung zur Benutzersammlung zu gelangen.

Hat jemand am Frontend mit einem Kontextkonsumenten reagiert, um herauszufinden, wer der aktuelle Benutzer ist? Wenn ja, wie greifen Sie auf ihre Attribute im Authentifizierungsmodell zu und wie haben Sie auf die Attribute in der zugehörigen Benutzersammlung zugegriffen (wobei die docId im Benutzerdokument die UID aus der Authentifizierungstabelle ist)?

NÄCHSTER VERSUCH

Der nächste Versuch hat zu einem sehr seltsamen Ergebnis geführt.

Ich habe 2 separate Seiten, die Kontextkonsumenten sind. Der Unterschied zwischen ihnen besteht darin, dass eine eine Funktion und die andere eine Klassenkomponente ist.

In der Funktionskomponente kann ich {authUser.email} rendern. Wenn ich versuche, dasselbe in der Klassenkomponente zu tun, wird eine Fehlermeldung angezeigt, die besagt:

TypeError: Die Eigenschaft 'email' von null kann nicht gelesen werden

Dieser Fehler stammt aus derselben Sitzung mit demselben angemeldeten Benutzer.

Hinweis: Während in der Firebase-Dokumentation angegeben ist, dass eine aktuelle User-Eigenschaft für die Authentifizierung verfügbar ist, konnte ich diese Funktion überhaupt nicht zum Laufen bringen.

Meine Funktionskomponente hat:

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;

Während ich nicht zu den Benutzersammlungsattributen gelangen kann, bei denen die docId im Benutzerdokument mit der UID des authentifizierten Benutzers identisch ist, kann ich von dieser Komponente aus das E-Mail-Attribut in der Auth-Sammlung für diesen Benutzer ausgeben.

Während der Firebase-Dokumentation diesen Rat für die Verwaltung von Benutzern und den Zugriff auf Attribute hier enthält, habe ich keine Möglichkeit gefunden, diesen Ansatz in Reaktion zu implementieren. Jede Variation eines Versuchs, dies zu tun, sowohl indem Helfer in meiner firebase.js erstellt werden als auch versucht wird, in einer Komponente von vorne zu beginnen, führt zu Fehlern beim Zugriff auf firebase. Ich kann jedoch eine Liste der Benutzer und der zugehörigen Benutzersammlungsinformationen erstellen (ich kann keinen Benutzer basierend darauf finden, wer der authUser ist).

Meine Klassenkomponente hat:

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;

In meinem AuthContext.Provider - habe ich:

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;

NÄCHSTER VERSUCH

Es ist wirklich seltsam, dass ich bei diesem Versuch versuche, die Werte, die in der Datenbank vorhanden sind, zu konsolidieren, und der Wert von name als 'undefined' zurückgegeben wird, wobei die Datenbank eine Zeichenfolge enthält.

Dieser Versuch hat:

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

NÄCHSTER VERSUCH

Dieser Versuch ist also umständlich und verwendet nicht die Helfer oder Snapshot-Abfragen, die ich oben verwendet habe, sondern protokolliert die Attribute des Benutzersammlungsdokuments wie folgt in der Konsole:

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

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

    } 

Was ich aber nicht tun kann, ist einen Weg zu finden, diesen Namen in der jsx zu rendern

Wie drucken Sie die Ausgabe tatsächlich?

Wenn ich es versuche:

{ 


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

                }

Ich erhalte eine Fehlermeldung:

TypeError: this.props.firebase.db.collection (...). Doc (...). Get (...). Data ist keine Funktion

Wenn ich es versuche:

{ 



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

Ich erhalte eine Fehlermeldung:

Zeile 281: 23: Erwartete eine Zuweisung oder einen Funktionsaufruf und sah stattdessen einen Ausdruck ohne unbenutzte Ausdrücke

Wenn ich es versuche:

{ 


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

Die Fehlermeldung lautet:

Erwartete eine Zuweisung oder einen Funktionsaufruf und sah stattdessen einen Ausdruck

Ich bin bereit, den Versuch aufzugeben, herauszufinden, wie die Snapshot-Abfragen funktionieren - wenn ich nur den Namen der Benutzersammlung erhalten kann, die auf dem Bildschirm gerendert werden soll. Kann jemand bei diesem Schritt helfen?

NÄCHSTER VERSUCH

Ich habe diesen Beitrag gefunden . Es gibt eine gute Erklärung dafür, was passieren muss, aber ich kann es nicht wie gezeigt implementieren, da componentDidMount nicht weiß, was authUser ist.

Mein aktueller Versuch lautet wie folgt: Wie derzeit geschrieben, ist authUser jedoch ein Wrapper für den Rückgabewert, und das Segment componentDidMount weiß nicht, was authUser ist.

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

NÄCHSTER VERSUCH

Als nächstes habe ich versucht, die Route für das Dashboard in AuthContext.Consumer zu verpacken, damit die gesamte Komponente sie verwenden kann. Dadurch konnte ich in der Funktion componentDidMount auf den angemeldeten Benutzer zugreifen.

Ich habe die Route geändert zu:

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

und entfernte den Verbraucher aus der Renderanweisung der Dashboard-Komponente.

Dann habe ich in componentDidMount in der Dashboard-Komponente versucht:

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

Wenn ich das versuche, erhalte ich eine Fehlermeldung:

FirebaseError: Für die Funktion CollectionReference.doc () muss das erste Argument vom Typ einer nicht leeren Zeichenfolge sein, es war jedoch: ein benutzerdefiniertes DocumentReference-Objekt

NÄCHSTER VERSUCH Die folgenden Personen scheinen in der ersten vorgeschlagenen Lösung etwas Hilfreiches zu finden. Ich konnte nichts Nützliches darin finden, aber wenn ich seine Vorschläge durchlese, habe ich Schwierigkeiten zu sehen, wie das Beispiel in der Firebase-Dokumentation (es gibt nicht an, wie der .doc () -Anforderung ein: uid-Wert zugewiesen werden soll ), was wie folgt ist:

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!");
      }

unterscheidet sich grundlegend von meinem Versuch in der Funktion componentDidMount, nämlich:

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

    );  
  }

Vielleicht ist das Lösen dieses Schritts ein Hinweis, der dazu beiträgt, dies zu einem Ergebnis zu führen. Kann jemand eine bessere Firestore-Dokumentation finden, um zu zeigen, wie ein Benutzersammlungsdatensatz mithilfe einer angemeldeten Benutzer-Listener-UID abgerufen wird?

Zu diesem Zweck kann ich dem Beispiel des FriendlyEats- Codelabors entnehmen, dass versucht wird, dem ID-Suchwert im Code eine doc.id zuzuweisen. Ich weiß nicht, in welcher Sprache dieser Code geschrieben ist - aber er sieht ähnlich aus wie das, was ich versuche - ich kann einfach nicht sehen, wie ich von diesem Beispiel zu etwas übergehen kann, mit dem ich arbeiten kann.

display: function(doc) {
      var data = doc.data();
      data['.id'] = doc.id;
      data['go_to_restaurant'] = function() {
        that.router.navigate('/restaurants/' + doc.id);
      };
Mel
quelle
Zu Ihrer Information, Ihre Terminologie ist nicht ganz richtig und macht es schwierig, diese Frage zu lesen. In Firebase gibt es nichts, was als "Tabelle" bezeichnet wird. In Firebase Auth gibt es nur Benutzer - keine "Authentifizierungstabelle". In Firestore befinden sich Sammlungen und Dokumente in diesen Sammlungen, jedoch keine Tabellen. Ich versuche herauszufinden, wo Sie stecken bleiben und wie der Code, den Sie gezeigt haben, nicht so funktioniert, wie Sie es erwarten, aber ich setze ihn einfach nicht zusammen. Bearbeiten Sie die Frage, um eine Standardterminologie zu verwenden, die Sie in den Dokumenten finden würden, und machen Sie sich klarer darüber, was nicht wie erwartet funktioniert.
Doug Stevenson
Gut - gerne ersetzen Sammlungen durch Tabellen. Der Punkt ist immer noch der gleiche.
Mel
Mein Hauptpunkt ist, dass ich Ihren Standpunkt nicht wirklich verstehen konnte und die Terminologie nicht geholfen hat. Können Sie genauer erklären, was in dem von Ihnen gezeigten Code nicht funktioniert? Hat etwas nicht wie erwartet funktioniert? Irgendwelche Fehler oder Debug-Protokolle zur Veranschaulichung?
Doug Stevenson
Nichts funktioniert. Ich erwarte, einen Weg zu finden, um über den authUser-Listener auf die Details der Benutzersammlung zuzugreifen. authUser wird im Kontexthandler und in der zugehörigen Klassenmethode definiert, die auf Änderungen an der Methode wartet. Ich komme nicht an den Attributen in der Authentifizierungssammlung vorbei. Ich versuche, Zugriff auf die zugehörige Benutzersammlung im Firestore zu erhalten. Keine Protokolle. Nur Fehlermeldungen, die besagen, dass das Feld undefiniert ist.
Mel
1
Ich schlage vor, mit einer einfachen Aufgabe zu beginnen, diese zum Laufen zu bringen, um Erfahrungen zu sammeln, und diese dann auf komplexere Probleme anzuwenden, wie Sie sie jetzt haben. An der Dokumentation ist nichts grundlegend Falsches (ich weiß, weil ich sie ständig benutze und Menschen dabei helfe). Um Hilfe bei Stack Overflow zu erhalten, müssen Sie ein bestimmtes Problem veranschaulichen, idealerweise ein MCVE, mit dem jeder das Problem reproduzieren kann. Nur zu sagen "Ich kann es nicht zum Laufen bringen" ist nicht genug. stackoverflow.com/help/minimal-reproducible-example
Doug Stevenson

Antworten:

5

Aus der letzten Zeile Ihrer Frage ( users = () => this.db.collection('users');) geht hervor, dass die Sammlung aufgerufen wird users, in der Sie zusätzliche Informationen zu den Benutzern speichern, und dass ein Benutzerdokument in dieser Sammlung die Benutzer-ID (UID) als Dokument-ID verwendet.

Folgendes sollte den Trick machen (ungetestet):

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()Wenn wir also innerhalb des durch die Methode festgelegten Beobachters feststellen, dass der Benutzer angemeldet ist (dh in if (authUser) {}), verwenden wir dessen uid, um das eindeutige Dokument abzufragen, das diesem Benutzer in der usersSammlung entspricht (siehe Lesen eines Dokuments und das Dokument des get()Methode).

Renaud Tarnec
quelle
Stimmt also etwas nicht mit der Art und Weise, wie ich onAuthUserListener definiert habe? Was soll ich dann tun, wenn ich Ihre Änderungen an dieser Methode versuche, um von authUser zur Benutzersammlung zu gelangen?
Mel
"Stimmt also etwas nicht mit der Art und Weise, wie ich onAuthUserListener definiert habe?" -> nicht nach dem, was ich sehen kann. "Was soll ich tun, um von authUser zur Benutzersammlung zu gelangen?" -> Wenn ich das richtig verstehe, möchten Sie EIN Dokument erhalten, nicht die Sammlung. Der Code in der Antwort sollte funktionieren.
Renaud Tarnec
Ich möchte authUser erhalten - ist Ihr Code eine Verbesserung meines Versuchs? Ich kann keinen Weg finden, mit dem der authUser Zugriff auf die Benutzersammlung mit derselben UID erhält. Ich versuche, Ihren Code-Vorschlag zu verstehen - wie sich dieser als ersten Schritt gegenüber meinem verbessert. Können Sie bitte feststellen, welches Stück davon die Verbesserung / Korrektur ist? Vielen Dank
Mel
Was passiert, wenn Sie es tun this.auth.onAuthStateChanged(authUser => { if (authUser) {console.log(authUser.uid) })?
Renaud Tarnec
Ich kann alle authUser-Eigenschaften (die Authentifizierungssammlungsdaten) ausgeben. Ich kann nicht auf die Benutzerdaten aus dem zugehörigen Dokument in der Benutzersammlung zugreifen, das die UID als ID hat
Mel
1

Ich habe eine Theorie, die Sie testen sollen.

Ich denke, wenn Sie in next(authUser)Ihrem onAuthStateChangedHandler aufrufen , stößt er während seiner Ausführung auf einen Fehler (z. B. cannot read property 'name' of undefined at ...).

Der Grund, warum Ihr Code nicht wie erwartet funktioniert, liegt darin, wo Sie anrufen next(authUser) , liegt darin, dass er sich innerhalbthen() Promise-Kette . Alle Fehler, die in ein Versprechen geworfen werden, werden abgefangen und führen dazu, dass das Versprechen abgelehnt wird. Wenn ein Versprechen abgelehnt wird, ruft es die angehängten Fehlerbehandlungsroutinen mit dem Fehler auf. Die betreffende Promise-Kette verfügt derzeit über keinen solchen Fehlerbehandler.

Wenn ich dich verloren habe, lies diesen Blog-Beitrag für einen Promises-Crashkurs und komm dann zurück.

Was können wir also tun, um eine solche Situation zu vermeiden? Am einfachsten wäre es, next(authUser) außerhalb des then()Bereichs des Promise- Handlers anzurufen . Wir können dies mit tunwindow.setTimeout(function) .

In Ihrem Code würden Sie also ersetzen

next(authUser)

mit

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

Dadurch werden Fehler wie gewohnt ausgelöst, anstatt von der Promise-Kette erfasst zu werden.

Wichtig ist, dass Sie keinen Catch-Handler haben, der userDocRef.get()Fehler behandelt. Also einfach .catch(() => setTimeout(fallback))am Ende hinzufügenthen() damit Ihr Code die Fallback-Methode verwendet, wenn ein Fehler auftritt.

Am Ende haben wir also:

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

Bearbeiteter Code

Die obige Erklärung sollte es Ihnen ermöglichen, Ihren Code zu reparieren, aber hier ist meine Version von Ihrem Firebase Klasse mit verschiedenen benutzerfreundlichen Änderungen.

Verwendungszweck:

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
}

Klassendefinition:

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

Beachten Sie, dass die onUserDataListenerMethode in dieser Version die Funktion zum Abbestellen von zurückgibt onAuthStateChanged. Wenn Ihre Komponente nicht gemountet ist, sollten Sie alle relevanten Listener trennen, damit kein Speicherverlust auftritt oder fehlerhafter Code im Hintergrund ausgeführt wird, wenn er nicht benötigt wird.

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();
  }
}
samthecodingman
quelle
Vielen Dank! Ich werde es heute Abend versuchen. Ich bin gespannt, wie auch immer. Ich werde in Kürze mit Feedback zurückkommen.
Mel
Hallo Sam, danke, dass du diesen Vorschlag gemacht hast. Ich habe einige Zeit gebraucht, um die von Ihnen verlinkte Dokumentation durchzulesen, und ich habe ein paar Versuche unternommen. Obwohl ich für die Hilfe dankbar bin, hat dies mein Problem nicht gelöst. Wenn ich versuche, auf die Benutzersammlungsattribute zuzugreifen, wird immer noch die folgende Fehlermeldung angezeigt: TypeError: Die Eigenschaft 'user' von undefined kann nicht gelesen werden
Mel
@Mel Wurden Sie beim Ausführen des Originalcodes über den TypeError informiert? Dies ist das erste Mal, dass Sie es erwähnt haben. Dies bedeutet, dass dieser Code den Fehler außerhalb des Bereichs des Versprechens ausgelöst hat. Können Sie die Ausgabe von bereitstellen console.log(snapshot.data())?
Samthecodingman
Ich habe es versucht - die Fehlermeldung lautet: TypeError: snapshot.data ist keine Funktion
Mel
Ich werde weiter versuchen, es zu bewegen - vielleicht versuche ich nicht, dies an einem guten Ort zu protokollieren
Mel
0

In Ihrer AuthContext.ProviderImplementierung greifen Sie onAuthStateChanged direkt auf den Listener des SDK zu :

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

Dies sollte geändert werden, um die onAuthUserListeneraus Ihrer Hilfsklasse zu verwenden:

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

In Bezug auf Protokollnachrichten, die mit vielen zufälligen Eigenschaften gefüllt sind, liegt dies daran, dass das firebase.UserObjekt sowohl über eine öffentliche API als auch über eine Implementierung mit einer Reihe privater Eigenschaften und Methoden verfügt, die beim Kompilieren minimiert werden. Da diese minimierten Eigenschaften und Methoden nicht explizit als nicht aufzählbar markiert sind, sind sie in jeder Protokollausgabe enthalten. Wenn Sie stattdessen nur die Teile protokollieren möchten, die tatsächlich nützlich sind, können Sie das Objekt mit folgenden Elementen destrukturieren und umstrukturieren:

// 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
quelle
Vielen Dank an @samthecodingman, dass Sie weiterhin versucht haben, mir zu helfen. Ich habe mir das angeschaut. Mein Ziel ist es, die UID des authUser zu lesen, damit ich damit Attribute der zugehörigen Benutzersammlung für diesen Benutzer abrufen kann (Name in der Benutzersammlung hat mehr als displayName in der Firebase-Auth-Sammlung - also versuche ich es nicht Lesen Sie die Eigenschaften der Authentifizierungstabelle.
Mel
Die vorgeschlagene Änderung der Listener-Komponente componentDidMount hat keinen Fehler ausgelöst, aber nicht funktioniert. Wenn versucht wird, den Wert von authUser in der Dashboard-Komponente mit diesem Listener zu protokollieren, wird eine Fehlermeldung angezeigt, dass authUser nicht definiert ist. Ich erhalte diesen Fehler nicht, wenn ich den componentDidMount für den AuthContext.Provider so verwende, wie ich ihn definiert habe. Vielen Dank für die Informationen zu den zufälligen Eigenschaften in den Protokollnachrichten.
Mel
@ Mel Kannst du bestätigen, dass die letzte Zeile deiner DashboardDatei export default withAuthentication(Dashboard);(und nicht withFirebase) ist
samthecodingman
Bestätigt. withFirebase ist in withAuthentication integriert - wird also über dieses HOC erfasst.
Mel
@ Mel Kannst du die Nachrichten überprüfen, die ich dir am
Uhr
0

Wenn jemand ähnlich stecken bleibt, habe ich hier die Lösung gefunden: Firebase & React: CollectionReference.doc () Argumenttyp

Bei der Seitenaktualisierung funktioniert dies nicht (es wird immer noch der Fehler ausgegeben, dass die UID null ist). Wenn Sie jedoch auf Hooks mit useEffect reagieren, sollte die Funktion componentDidMount durch eine Kombination aus Mount und Update ersetzt werden. Ich versuche das als nächstes.

Mel
quelle