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