Come si ottengono i dettagli del db dell'utente relativi a un autore in Firestore?


10

Sto cercando di capire come ottenere un nome utente che è un attributo archiviato in una raccolta utenti, che è stato unito agli attributi creati dal modello di autenticazione Firebase.

Posso accedere ad authUser - il che mi dà i campi limitati raccolti da firebase nello strumento di autenticazione, e quindi sto provando a passare da lì alla raccolta di utenti correlati (che utilizza lo stesso uid).

Ho un consumatore di contesto di reazione con:

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

Quindi nel mio componente sto cercando di usare:

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

Nel mio firebase.js, penso di aver provato a unire gli attributi authUser dal modello di autenticazione con gli attributi della raccolta utente come segue:

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

Non riesco a trovare un modo per ottenere dall'autenticatore (che funziona per portarmi agli attributi di autenticazione) - alla raccolta utente che ha un ID con lo stesso uid dalla raccolta di autenticazione.

Ho visto questo post , che sembra avere lo stesso problema e ho cercato di capire a cosa dovrebbe suggerire la risposta, ma non riesco a trovare un modo che funzioni dalla raccolta di autenticazione alla raccolta di utenti e non so cosa stia facendo l'unione se non mi dà accesso agli attributi sulla raccolta utenti da authUser.

Ho provato a usare un helper nel mio firebase.js per darmi un utente da un uid - ma questo non sembra aiutare neanche.

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

Prossimo tentativo

Per aggiungere altro sfondo, ho creato un componente di test in grado di registrare (ma non renderizzare) authUser, come segue:

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;

Il registro mostra i dettagli relativi a uid, e-mail, ecc. Nei registri, ma è incluso in un lungo elenco di elementi, molti dei quali sono preceduti da 1 o 2 lettere (non riesco a trovare una chiave per scoprire cosa ciascuno di questi prefissi lettere significano). Esempio estratto di seguito:

inserisci qui la descrizione dell'immagine

AGGIORNAMENTO SU QUESTO COMMENTO:

In precedenza, ho detto: i campi per uid, e-mail ecc. Non sembrano essere nidificati sotto questi prefissi, ma se provo a:

 console.log(authUser.email)

, Viene visualizzato un errore che dice:

TypeError: impossibile leggere la proprietà 'email' di null

L'aggiornamento: ho appena realizzato che nel registro della console, devo espandere un menu a discesa che è etichettato:

Q {I: Array (0), l:

per vedere l'attributo email. Qualcuno sa a cosa allude questo jibberish? Non riesco a trovare una chiave per capire cosa significhi Q, I o I, per sapere se dovrei fare riferimento a queste cose per ottenere gli attributi rilevanti nella tabella di autenticazione. Forse, se riesco a capirlo, posso trovare un modo per accedere alla raccolta utenti utilizzando l'uid della raccolta Autenticazione.

Qualcuno ha usato la reazione sul front-end, con un consumatore di contesto per scoprire chi è l'utente attuale? In tal caso, come si accede ai relativi attributi nel modello di autenticazione e come si accede agli attributi nella raccolta Utente correlata (dove il docId nel documento Utente è l'UID dalla tabella di autenticazione)?

PROSSIMO TENTATIVO

Il prossimo tentativo ha prodotto un risultato molto strano.

Ho 2 pagine separate che sono consumatori contestuali. La differenza tra loro è che uno è una funzione e l'altro è un componente di classe.

Nel componente funzione, posso renderizzare {authUser.email}. Quando provo a fare la stessa cosa nel componente di classe, ricevo un errore che dice:

TypeError: impossibile leggere la proprietà 'email' di null

Questo errore proviene dalla stessa sessione con lo stesso utente connesso.

Nota: mentre la documentazione di firebase dice che una proprietà currentUser è disponibile su auth - non sono stato in grado di farlo funzionare affatto.

Il mio componente di funzione ha:

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;

Anche se non riesco ad accedere agli attributi della raccolta Utente in cui il docId nel documento utente è lo stesso dell'Uid dell'utente autenticato, da questo componente, posso generare l'attributo e-mail sulla raccolta auth per questo utente.

Mentre la documentazione di Firebase fornisce questi consigli per la gestione degli utenti e l'accesso agli attributi qui, non ho trovato il modo di implementare questo approccio in reazione. Ogni variazione di un tentativo di fare questo, sia facendo aiuto in my firebase.js sia provando a ricominciare da zero in un componente produce errori nell'accedere a firebase. Tuttavia, posso produrre un elenco di utenti e le relative informazioni sulla raccolta degli utenti (non riesco a ottenere un utente in base a chi sia l'Autenticatore).

Il mio componente di classe ha:

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;

Nel mio AuthContext.Provider - ho:

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;

PROSSIMO TENTATIVO

È davvero strano che con questo tentativo, sto provando a registrare in console i valori che posso vedere esistono nel database e il valore del nome viene restituito come 'indefinito' dove il db ha una stringa al suo interno.

Questo tentativo ha:

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

PROSSIMO TENTATIVO

Quindi questo tentativo è goffo e non fa uso degli helper o delle query di istantanee che ho provato a usare sopra, ma registra gli attributi del documento di raccolta utenti sulla console come segue:

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

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

    } 

Quello che non posso fare è trovare un modo per rendere quel nome in jsx

Come si stampa effettivamente l'output?

Quando provo:

{ 


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

                }

Viene visualizzato un errore che dice:

TypeError: this.props.firebase.db.collection (...). Doc (...). Get (...). Data non è una funzione

Quando provo:

{ 



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

Viene visualizzato un errore che dice:

Riga 281: 23: si aspettava un incarico o una chiamata di funzione e invece ha visto un'espressione espressioni inutilizzate

Quando provo:

{ 


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

Il messaggio di errore dice:

Previsto un incarico o una chiamata di funzione e invece ho visto un'espressione

Sono pronto a rinunciare a provare a scoprire come far funzionare le query di snapshot, se riesco solo a ottenere il nome della raccolta utenti da visualizzare sullo schermo. Qualcuno può aiutare con quel passaggio?

PROSSIMO TENTATIVO

Ho trovato questo post . Ha una buona spiegazione di ciò che deve accadere, ma non riesco a implementarlo come mostrato perché componentDidMount non sa che cos'è Autenticatore.

Il mio tentativo attuale è il seguente - tuttavia, come attualmente scritto, authUser è un wrapper sul valore restituito - e il segmento componentDidMount non sa cosa sia 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);

PROSSIMO TENTATIVO

Successivamente, ho provato a racchiudere il percorso per il dashboard all'interno di AuthContext.Consumer in modo che l'intero componente potesse usarlo, permettendomi così di accedere all'utente connesso nella funzione componentDidMount.

Ho cambiato il percorso in:

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

e rimosso il consumatore dall'istruzione di rendering del componente dashboard.

Quindi in componentDidMount sul componente Dashboard, ho provato:

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

Quando provo questo, ricevo un errore che dice:

FirebaseError: Function CollectionReference.doc () richiede che il suo primo argomento sia di tipo stringa non vuota, ma era: un oggetto DocumentReference personalizzato

PROSSIMO TENTATIVO Le persone sottostanti sembrano trovare qualcosa di utile nella prima soluzione proposta. Non sono stato in grado di trovare nulla di utile in esso, ma rileggendo i suoi suggerimenti, sto lottando per vedere come l'esempio nella documentazione di Firebase (non rivela come dare un valore: uid alla richiesta .doc () ), che è il seguente:

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

è fondamentalmente diverso dal mio tentativo nella funzione componentDidMount, che è:

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

    );  
  }

Forse risolvere quel passo è un indizio che aiuterà a spostare questo verso un risultato. Qualcuno può trovare una migliore documentazione di firestore per mostrare come ottenere un record di raccolta utente utilizzando un uid listener utente registrato?

A tal fine, posso vedere dal laboratorio di codice FriendlyEats ad esempio , che v'è un tentativo di dare un doc.id al valore di ricerca id nel codice. Non so in che lingua è scritto questo codice - ma sembra simile a quello che sto cercando di fare - Non riesco proprio a vedere come passare da quell'esempio a qualcosa con cui so come lavorare.

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

Cordiali saluti, la tua terminologia non è del tutto corretta e rende difficile leggere questa domanda. In Firebase non c'è nulla chiamato "tabella". In Firebase Auth, ci sono solo utenti - nessuna "tabella di autenticazione". In Firestore ci sono raccolte e documenti all'interno di quelle raccolte, ma nessuna tabella. Sto cercando di capire dove ti blocchi e come il codice che hai mostrato non funziona come ti aspetti, ma non lo sto mettendo insieme. Valuta la possibilità di modificare la domanda in modo da utilizzare una terminologia più standard di quella che troverai nei documenti ed essere più chiaro su ciò che non funziona come previsto.
Doug Stevenson,

Bene - felice di sostituire le tabelle con le raccolte. Il punto è sempre lo stesso.
Mel

Il mio punto principale è che non potevo davvero capire il tuo punto e la terminologia non ha aiutato. Potresti spiegare in modo più dettagliato cosa non funziona nel codice che hai mostrato? Qualcosa non ha funzionato come previsto? Eventuali errori o log di debug da illustrare?
Doug Stevenson,

Niente funziona. Mi aspetto di trovare un modo per accedere ai dettagli della raccolta utente dal listener authUser. authUser è definito nel gestore di contesto e nel metodo di classe correlato che ascolta le modifiche al metodo. Non riesco a superare gli attributi nella raccolta di autenticazione: sto cercando di accedere alla raccolta di utenti correlati nel negozio di alimentari. Nessun registro. Solo messaggi di errore che indicano che il campo non è definito.
Mel

1
Suggerisco di iniziare con un compito semplice, farlo funzionare per acquisire esperienza, quindi applicarlo a problemi più complessi come quello che hai ora. Non c'è nulla di fondamentalmente errato nella documentazione (lo so, perché lo uso sempre e aiuto le persone con lo stesso). Per ricevere assistenza su Stack Overflow, dovrai illustrare un problema specifico, idealmente un MCVE che chiunque può utilizzare per riprodurre il problema. Basta dire "Non riesco a farlo funzionare" non è abbastanza. stackoverflow.com/help/minimal-reproducible-example
Doug Stevenson

Risposte:


5

Capisco dall'ultima riga della tua domanda ( users = () => this.db.collection('users');) che viene chiamata la raccolta in cui memorizzi informazioni extra sugli utenti userse che un documento utente in questa raccolta utilizza userId (uid) come docId.

Quanto segue dovrebbe fare il trucco (non testato):

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

Quindi, all'interno dell'osservatore impostato tramite il onAuthStateChanged()metodo, quando rileviamo che l'utente ha effettuato l'accesso (ovvero in if (authUser) {}), lo utilizziamo uidper interrogare il documento univoco corrispondente a questo utente nella usersraccolta (vedi leggi un documento e il documento del get()metodo).


Quindi c'è qualcosa di sbagliato nel modo in cui ho definito onAuthUserListener? Quindi, se provo le tue modifiche a quel metodo, cosa dovrei fare per accedere alla raccolta utenti da authUser?
Mel

"Quindi c'è qualcosa di sbagliato nel modo in cui ho definito onAuthUserListener?" -> non da quello che posso vedere. "cosa dovrei fare per accedere alla raccolta utenti da authUser?" -> Se ho capito bene, vuoi ottenere UN documento, non la raccolta. Il codice nella risposta dovrebbe funzionare.
Renaud Tarnec,

Voglio ottenere authUser: il tuo codice è un miglioramento rispetto al mio tentativo? Non riesco a trovare un modo che funzioni per fare in modo che l'autenticatore mi dia accesso alla raccolta utenti che ha lo stesso uid. Sto cercando di capire il tuo suggerimento sul codice - per come questo migliora il mio come primo passo. Per favore, puoi identificare quale parte del miglioramento / correzione? Grazie
Mel

Cosa succede se lo fai this.auth.onAuthStateChanged(authUser => { if (authUser) {console.log(authUser.uid) })?
Renaud Tarnec,

Posso emettere tutte le proprietà di authUser (i dati della raccolta di autenticazione). Non riesco a ottenere i dati dell'utente dal documento correlato nella raccolta utenti che ha l'ID come ID
Mel

1

Ho una teoria che vorrei che tu mettessi alla prova.

Penso che quando si chiama next(authUser)all'interno del onAuthStateChangedgestore, durante la sua esecuzione si verifica un errore (come cannot read property 'name' of undefined at ...).

Il motivo per cui il tuo codice non funziona come previsto è perché dove chiami next(authUser), è all'interno dithen() della catena Promise. Eventuali errori lanciati all'interno di una Promessa verranno colti e causeranno il rifiuto della Promessa. Quando una promessa viene rifiutata, chiamerà i gestori degli errori allegati con l'errore. La catena Promise in questione al momento non ha questo gestore di errori.

Se ti ho perso, leggi questo post sul blog per un corso intensivo Promises e poi torna indietro.

Quindi cosa possiamo fare per evitare una situazione del genere? Il più semplice sarebbe chiamare next(authUser) al di fuorithen() dell'ambito del gestore Promise . Possiamo farlo usando window.setTimeout(function).

Quindi nel tuo codice, lo sostituiresti

next(authUser)

con

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

Ciò genererà eventuali errori normalmente anziché essere catturati dalla catena Promise.

È importante sottolineare che non hai un gestore di cattura che gestisce quando userDocRef.get()fallisce. Quindi basta aggiungere .catch(() => setTimeout(fallback))alla fine di in then()modo che il codice utilizzi il metodo di fallback in caso di errori.

Quindi finiamo con:

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

Codice modificato

La spiegazione sopra dovrebbe permetterti di correggere il tuo codice, ma ecco la mia versione della tua Firebaseclasse con varie modifiche di facilità d'uso.

Uso:

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
}

Definizione della classe:

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

Si noti che in questa versione, il onUserDataListenermetodo restituisce la funzione di annullamento dell'iscrizione onAuthStateChanged. Quando il componente è smontato, è necessario scollegare tutti i listener pertinenti in modo da non avere una perdita di memoria o avere il codice rotto in esecuzione in background quando non è necessario.

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

Grazie! Ci proverò stasera. Sono entusiasta di conoscere questo in entrambi i modi. Tornerò presto con un feedback.
Mel

Ciao Sam, grazie per aver offerto questo suggerimento. Mi sono preso un po 'di tempo per leggere la documentazione che hai collegato e ho fatto qualche tentativo. Mentre sono grato per l'aiuto, questo non ha risolto il mio problema. Quando provo ad accedere agli attributi della raccolta utenti, ricevo ancora un errore che dice: TypeError: Impossibile leggere la proprietà 'user' di undefined
Mel

@Mel Quando hai eseguito il codice originale, sei stato informato del TypeError? Questa è la prima volta che lo menzionate. Ciò significa che questo codice ha comportato il compito di eliminare l'errore al di fuori dell'ambito della Promessa. Potete fornire l'output di console.log(snapshot.data())?
samthecodingman,

Ho provato questo - il messaggio di errore dice: TypeError: snapshot.data non è una funzione
Mel

Continuerò a provare a spostarlo - forse non sto cercando di registrarlo in un buon posto
Mel

0

Nella tua AuthContext.Providerimplementazione, accedi onAuthStateChanged direttamente al listener dell'SDK :

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

Questo dovrebbe essere cambiato per usare il onAuthUserListenerdalla tua classe helper:

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

Per quanto riguarda i messaggi di log riempiti con molte proprietà casuali, ciò è dovuto al fatto che l' firebase.Useroggetto ha sia un'API pubblica sia un'implementazione con un numero di proprietà e metodi privati ​​minimizzati durante la compilazione. Poiché queste proprietà e metodi minimizzati non sono esplicitamente contrassegnati come non enumerabili, sono inclusi in qualsiasi output del registro. Se invece si desidera registrare solo le parti effettivamente utili, è possibile destrutturare e ristrutturare l'oggetto utilizzando:

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

Grazie @samthecodingman per aver continuato a provare ad aiutarmi. Ho dato un'occhiata a questo. Il mio obiettivo è di leggere l'utilità di authUser in modo da poterlo utilizzare per ottenere gli attributi della raccolta di utenti correlati per quell'utente (il nome nella raccolta di utenti ha più di displayName nella raccolta di autorizzazione della base di fuoco - quindi non sto provando a leggi le proprietà della tabella di autenticazione
Mel

La modifica suggerita alla funzione componentDidMount del listener non ha generato un errore, ma non ha funzionato. Quando si tenta di registrare il valore di authUser nel componente dashboard utilizzando questo listener - viene visualizzato un errore che indica che authUser non è definito. Non visualizzo questo errore quando utilizzo componentDidMount per AuthContext.Provider nel modo in cui l'ho definito. Grazie per le informazioni sulle proprietà casuali nei messaggi di registro.
Mel

@Mel Puoi confermare che l'ultima riga del tuo Dashboardfile è export default withAuthentication(Dashboard);(e non withFirebase)
samthecodingman

Confermato. withFirebase è incorporato in withAuthentication, quindi viene raccolto attraverso quell'HOC.
Mel

@Mel Puoi controllare i messaggi che ti ho inviato su Slack
samthecodingman il

0

Se qualcun altro è bloccato in modo simile, ho trovato la soluzione qui: Firebase & React: CollectionReference.doc () tipo di argomento

Non funziona sull'aggiornamento della pagina (genera ancora un errore che l'uid è nullo) ma reagisce agli hook per usareEffect dovrebbe sostituire la funzione componentDidMount con una combinazione di Mount e Update. Lo proverò dopo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.