Próbuję dowiedzieć się, jak uzyskać nazwę użytkownika, który jest atrybutem przechowywanym w kolekcji użytkownika, która została połączona z atrybutami utworzonymi przez model uwierzytelniania bazy ogniowej.
Mogę uzyskać dostęp do authUser - co daje mi ograniczone pola, które baza ogniowa zbiera w narzędziu uwierzytelniającym, a następnie próbuję dostać się stamtąd do powiązanej kolekcji użytkowników (która używa tego samego identyfikatora użytkownika).
Mam kontekstowego konsumenta reagującego na:
import React from 'react';
const AuthUserContext = React.createContext(null);
export default AuthUserContext;
Następnie w moim komponencie próbuję użyć:
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);
W moim firebase.js - myślę, że próbowałem scalić atrybuty authUser z modelu Uwierzytelniania z atrybutami kolekcji użytkowników w następujący sposób:
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();
}
});
Nie mogę znaleźć sposobu na przejście od authUser (który działa, aby dostać się do atrybutów Uwierzytelniania) - do kolekcji użytkownika, która ma identyfikator z tym samym identyfikatorem użytkownika z kolekcji Uwierzytelnianie.
Widziałem ten post , który wydaje się mieć ten sam problem, i próbowałem ustalić, na co ma sugerować odpowiedź - ale nie mogę znaleźć sposobu, który działałby, aby przejść z kolekcji uwierzytelniania do kolekcji użytkownika i nie wiem, co robi dla mnie scalanie, jeśli nie daje mi dostępu do atrybutów w kolekcji użytkowników z authUser.
Próbowałem użyć pomocnika w moim firebase.js, aby dać mi użytkownika z UID - ale to też nie pomaga.
user = uid => this.db.doc(`users/${uid}`);
users = () => this.db.collection('users');
Następna próba
Aby dodać więcej tła, stworzyłem komponent testowy, który może rejestrować (ale nie renderować) authUser w następujący sposób:
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;
Dziennik zawiera szczegółowe informacje dotyczące identyfikatora użytkownika, adresu e-mail itp., Ale znajduje się wśród długiej listy elementów - z których wiele jest poprzedzonych 1 lub 2 literami (nie mogę znaleźć klucza, aby dowiedzieć się, co każdy z tych prefiksów litery oznaczają). Przykład wyodrębniony poniżej:
AKTUALIZACJA TEGO KOMENTARZA:
Wcześniej powiedziałem: pola uid, e-mail itp. Nie wydają się być zagnieżdżone pod tymi prefiksami, ale jeśli spróbuję:
console.log(authUser.email)
, Pojawia się komunikat o błędzie:
TypeError: Nie można odczytać właściwości „email” o wartości null
Aktualizacja: Właśnie zdałem sobie sprawę, że w dzienniku konsoli muszę rozwinąć menu rozwijane z etykietą:
Q {I: Array (0), l:
aby zobaczyć atrybut e-mail. Czy ktoś wie, do czego nawiązuje ten jibberish? Nie mogę znaleźć klucza, aby dowiedzieć się, co oznacza Q, I lub l, aby wiedzieć, czy mam się odwoływać do tych rzeczy, aby uzyskać dostęp do odpowiednich atrybutów w tabeli uwierzytelniania. Może jeśli uda mi się to rozgryźć - znajdę sposób na dostanie się do kolekcji użytkowników za pomocą identyfikatora użytkownika z kolekcji Uwierzytelnianie.
Czy ktoś używał reagowania na interfejsie, z konsumentem kontekstowym, aby dowiedzieć się, kto jest bieżącym użytkownikiem? Jeśli tak, to w jaki sposób uzyskujesz dostęp do ich atrybutów w modelu Uwierzytelniania i jak uzyskałeś dostęp do atrybutów w powiązanej kolekcji Użytkowników (gdzie dokument ID w dokumencie użytkownika jest identyfikatorem UID z tabeli Uwierzytelniania)?
NASTĘPNA PRÓBA
Kolejna próba przyniosła bardzo dziwny wynik.
Mam 2 oddzielne strony, które są konsumentami kontekstowymi. Różnica między nimi polega na tym, że jedna jest funkcją, a druga jest składnikiem klasy.
W komponencie funkcji mogę renderować {authUser.email}. Kiedy próbuję zrobić to samo w komponencie klasy, pojawia się błąd:
TypeError: Nie można odczytać właściwości „email” o wartości null
Ten błąd pochodzi z tej samej sesji z tym samym zalogowanym użytkownikiem.
Uwaga: chociaż dokumentacja bazy ogniowej mówi, że właściwość currentUser jest dostępna podczas uwierzytelniania - nie udało mi się w ogóle sprawić, by działała.
Mój składnik funkcji ma:
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;
Chociaż nie mogę uzyskać dostępu do atrybutów kolekcji użytkownika, w których docId w dokumencie użytkownika jest taki sam jak identyfikator użytkownika uwierzytelnionego użytkownika, z tego komponentu mogę wypisać atrybut e-mail w kolekcji auth dla tego użytkownika.
Chociaż dokumentacja Firebase zawiera porady dotyczące zarządzania użytkownikami i uzyskiwania dostępu do atrybutów tutaj, nie znalazłem sposobu na wdrożenie tego podejścia w reakcji. Każda odmiana próby zrobienia tego, zarówno poprzez tworzenie pomocników w moim firebase.js, jak i próbę rozpoczęcia od zera w komponencie, powoduje błędy w dostępie do bazy ogniowej. Mogę jednak utworzyć listę użytkowników i powiązane z nimi informacje o kolekcji użytkowników (nie mogę uzyskać użytkownika na podstawie tego, kto jest authUser).
Mój komponent klasy ma:
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;
W moim AuthContext.Provider - mam:
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;
NASTĘPNA PRÓBA
To naprawdę dziwne, że przy tej próbie próbuję zapisać w konsoli wartości, które widzę w bazie danych, a wartość nazwy jest zwracana jako „niezdefiniowana”, w której db ma ciąg znaków.
Ta próba ma:
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);
NASTĘPNA PRÓBA
Ta próba jest niezdarna i nie wykorzystuje pomocników ani zapytań migawkowych, których próbowałem użyć powyżej, ale rejestruję atrybuty dokumentu kolekcji użytkownika w konsoli w następujący sposób:
{this.props.firebase.db.collection ('users'). doc (authUser.uid) .get ()
.then(doc => {
console.log(doc.data().name)
})
}
Nie mogę jednak znaleźć sposobu na wyrenderowanie tej nazwy w jsx
Jak faktycznie drukujesz wydruk?
Kiedy próbuję:
{
this.props.firebase.db.collection('users').doc(authUser.uid).get().data().name
}
Pojawia się komunikat o błędzie:
TypeError: this.props.firebase.db.collection (...). Doc (...). Get (...). Data nie jest funkcją
Kiedy próbuję:
{
this.props.firebase.db.collection('users').doc(authUser.uid).get()
.then(doc => {
console.log(doc.data().name),
<p>doc.data().name</p>
})
}
Pojawia się komunikat o błędzie:
Wiersz 281: 23: Oczekiwano przypisania lub wywołania funkcji i zamiast tego zobaczyłem wyrażenie niewykorzystane-wyrażenia
Kiedy próbuję:
{
this.props.firebase.db.collection('users').doc(authUser.uid).get("name")
.then(doc => {
console.log(doc.data().name),
<p>doc.data().name</p>
})
}
Komunikat o błędzie mówi:
Oczekiwano przypisania lub wywołania funkcji i zamiast tego zobaczyłem wyrażenie
Jestem gotów zrezygnować z próby znalezienia sposobu na uruchomienie zapytań migawkowych - jeśli mogę tylko wyświetlić nazwę kolekcji użytkowników do renderowania na ekranie. Czy ktoś może pomóc w tym kroku?
NASTĘPNA PRÓBA
Znalazłem ten post . Ma dobre wyjaśnienie tego, co musi się wydarzyć, ale nie mogę tego zaimplementować, jak pokazano, ponieważ componentDidMount nie wie, co to jest authUser.
Moja bieżąca próba jest następująca - jednak, jak obecnie napisano, authUser jest opakowaniem zwracanej wartości - a segment componentDidMount nie wie, co to jest 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);
NASTĘPNA PRÓBA
Następnie próbowałem zawinąć trasę dla deski rozdzielczej wewnątrz AuthContext.Consumer, aby cały komponent mógł z niej korzystać - pozwalając w ten sposób uzyskać dostęp do zalogowanego użytkownika w funkcji componentDidMount.
Zmieniłem trasę na:
<Route path={ROUTES.DASHBOARD} render={props => (
<AuthUserContext.Consumer>
{ authUser => (
<Dashboard authUser={authUser} {...props} />
)}
</AuthUserContext.Consumer>
)} />
i usunąłem konsumenta z instrukcji renderowania komponentu deski rozdzielczej.
Następnie w componentDidMount w komponencie Dashboard próbowałem:
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,
});
}
Gdy próbuję tego, pojawia się komunikat o błędzie:
FirebaseError: Funkcja CollectionReference.doc () wymaga, aby pierwszy argument był niepustym ciągiem typu, ale był to: niestandardowy obiekt DocumentReference
NASTĘPNA PRÓBA Ludzie poniżej wydają się znaleźć coś pomocnego w pierwszym proponowanym rozwiązaniu. Nie udało mi się znaleźć w nim nic przydatnego, ale czytając jego sugestie, staram się zobaczyć, jak przykład w dokumentacji bazy ogniowej (nie ujawnia, jak nadać wartość: uid żądaniu .doc () ), który jest następujący:
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!");
}
różni się zasadniczo od mojej próby w funkcji componentDidMount, która jest:
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");
}
);
}
Być może rozwiązanie tego kroku jest wskazówką, która pomoże to osiągnąć w wyniku. Czy ktoś może znaleźć lepszą dokumentację sklepu przeciwpożarowego, która pokaże, jak uzyskać rekord kolekcji użytkownika za pomocą zalogowanego identyfikatora użytkownika?
W tym celu z przykładu laboratorium kodu FriendlyEats widzę , że istnieje próba podania doc.id wartości wyszukiwania id w kodzie. Nie wiem, w jakim języku jest napisany ten kod - ale wygląda podobnie do tego, co próbuję zrobić - po prostu nie widzę, jak przejść od tego przykładu do czegoś, z czym wiem, jak pracować.
display: function(doc) {
var data = doc.data();
data['.id'] = doc.id;
data['go_to_restaurant'] = function() {
that.router.navigate('/restaurants/' + doc.id);
};