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