Я пытаюсь выяснить, как получить имя пользователя, которое является атрибутом, хранящимся в коллекции пользователей, которая была объединена с атрибутами, созданными моделью аутентификации firebase.
Я могу получить доступ к authUser - который дает мне ограниченные поля, которые firebase собирает в инструменте аутентификации, и затем я пытаюсь перейти оттуда к соответствующей коллекции пользователей (которая использует тот же uid).
У меня есть контекстный потребитель с:
import React from 'react';
const AuthUserContext = React.createContext(null);
export default AuthUserContext;
Тогда в моем компоненте я пытаюсь использовать:
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);
В моем firebase.js - я думаю, что я попытался объединить атрибуты authUser из модели Authentication с атрибутами пользовательской коллекции следующим образом:
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();
}
});
Я не могу найти способ получить от authUser (который работает, чтобы получить меня к атрибутам аутентификации) - к коллекции пользователей, которая имеет идентификатор с таким же идентификатором uid из коллекции аутентификации.
Я видел этот пост , в котором, похоже, возникла та же проблема, и пытался выяснить, на что намекает ответ, - но я не могу найти способ, который работает, чтобы попасть из коллекции Authentication в коллекцию пользователей и я не знаю, что делает для меня слияние, если оно не дает мне доступ к атрибутам в коллекции пользователей из authUser.
Я пытался использовать помощник в моем firebase.js, чтобы дать мне пользователя от uid - но это тоже не помогает.
user = uid => this.db.doc(`users/${uid}`);
users = () => this.db.collection('users');
Следующая попытка
Чтобы добавить больше фона, я сделал тестовый компонент, который может регистрировать (но не отображать) authUser следующим образом:
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;
В журнале отображаются подробные сведения о uid, электронной почте и т. Д. В журналах, но он находится среди длинного списка элементов, многие из которых начинаются с 1 или 2 букв (я не могу найти ключ, чтобы узнать, какой у каждого из этих префиксов буквы означают). Пример извлечен ниже:
ОБНОВЛЕНИЕ НА ЭТОМ КОММЕНТАРИИ:
Ранее я говорил: поля для uid, email и т. Д., Похоже, не вложены в эти префиксы, но если я попытаюсь:
console.log(authUser.email)
Я получаю сообщение об ошибке:
TypeError: Невозможно прочитать свойство 'email' со значением NULL
Обновление: я только что понял, что в журнале консоли мне нужно развернуть выпадающее меню с надписью:
Q {I: Array (0), l:
чтобы увидеть атрибут электронной почты. Кто-нибудь знает, на что намекает этот бред? Я не могу найти ключ, чтобы выяснить, что означает Q, I или l, чтобы узнать, должен ли я ссылаться на эти вещи, чтобы добраться до соответствующих атрибутов в таблице Authentication. Может быть, если я смогу это выяснить - я найду способ добраться до коллекции пользователей, используя uid из коллекции Authentication.
Кто-нибудь использовал ответ на интерфейсе, с потребителем контекста, чтобы узнать, кто является текущим пользователем? Если да, то как вы получаете доступ к их атрибутам в модели аутентификации и как вы обращались к атрибутам в соответствующей коллекции пользователей (где docId в документе пользователя - это идентификатор пользователя из таблицы аутентификации)?
СЛЕДУЮЩАЯ ПОПЫТКА
Следующая попытка дала очень странный результат.
У меня есть 2 отдельные страницы, которые являются потребителями контекста. Разница между ними заключается в том, что один является функцией, а другой - компонентом класса.
В компоненте функции я могу сделать {authUser.email}. Когда я пытаюсь сделать то же самое в компоненте класса, я получаю сообщение об ошибке:
TypeError: Невозможно прочитать свойство 'email' со значением NULL
Эта ошибка происходит из того же сеанса с тем же вошедшим в систему пользователем.
Примечание: хотя в документации по firebase говорится, что свойство currentUser доступно для auth, я не смог заставить его работать вообще.
Мой функциональный компонент имеет:
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;
Хотя я не могу добраться до атрибутов коллекции User, где docId в документе пользователя совпадает с uid аутентифицированного пользователя, из этого компонента я могу вывести атрибут email в коллекции auth для этого пользователя.
Пока документация Firebase предоставляет этот совет для управления пользователями и доступа к атрибутам, я не нашел способа реализовать этот подход в реакции. Каждый вариант попытки сделать это, как с помощью помощников в моем firebase.js, так и с попытки начать с нуля в компоненте, приводит к ошибкам доступа к firebase. Тем не менее, я могу создать список пользователей и связанную с ними информацию о коллекции пользователей (я не могу получить пользователя на основе того, кто является authUser).
Мой компонент класса имеет:
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;
В моем AuthContext.Provider - у меня есть:
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;
СЛЕДУЮЩАЯ ПОПЫТКА
Действительно странно, что с этой попыткой я пытаюсь сохранить в журнале значения, которые, как я вижу, существуют в базе данных, и значение name возвращается как «неопределенное», где в базе данных есть строка.
Эта попытка имеет:
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);
СЛЕДУЮЩАЯ ПОПЫТКА
Так что эта попытка неуклюжа и не использует хелперы или запросы моментальных снимков, которые я пытался использовать выше, но регистрирует атрибуты документа пользовательской коллекции на консоли следующим образом:
{this.props.firebase.db.collection ('users'). doc (authUser.uid) .get ()
.then(doc => {
console.log(doc.data().name)
})
}
Что я не могу сделать, так это найти способ визуализации этого имени в jsx
Как вы на самом деле печатать вывод?
Когда я пытаюсь:
{
this.props.firebase.db.collection('users').doc(authUser.uid).get().data().name
}
Я получаю сообщение об ошибке:
Ошибка типа: this.props.firebase.db.collection (...). Doc (...). Get (...). Data не является функцией
Когда я пытаюсь:
{
this.props.firebase.db.collection('users').doc(authUser.uid).get()
.then(doc => {
console.log(doc.data().name),
<p>doc.data().name</p>
})
}
Я получаю сообщение об ошибке:
Строка 281: 23: ожидал присваивания или вызова функции и вместо этого увидел выражение no-unused-expression
Когда я пытаюсь:
{
this.props.firebase.db.collection('users').doc(authUser.uid).get("name")
.then(doc => {
console.log(doc.data().name),
<p>doc.data().name</p>
})
}
Сообщение об ошибке гласит:
Ожидал присваивания или вызова функции и вместо этого увидел выражение
Я готов отказаться от попыток выяснить, как заставить работать запросы моментальных снимков - если я могу просто получить имя пользовательской коллекции для отображения на экране. Кто-нибудь может помочь с этим шагом?
СЛЕДУЮЩАЯ ПОПЫТКА
Я нашел этот пост . В нем есть хорошее объяснение того, что должно произойти, но я не могу реализовать это, как показано, потому что componentDidMount не знает, что такое authUser.
Моя текущая попытка заключается в следующем - однако, как написано в настоящее время, authUser является оберткой возвращаемого значения - и сегмент componentDidMount не знает, что такое 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);
СЛЕДУЮЩАЯ ПОПЫТКА
Затем я попытался обернуть маршрут для панели мониторинга внутри AuthContext.Consumer, чтобы его мог использовать весь компонент, что позволило мне получить доступ к зарегистрированному пользователю в функции componentDidMount.
Я изменил маршрут на:
<Route path={ROUTES.DASHBOARD} render={props => (
<AuthUserContext.Consumer>
{ authUser => (
<Dashboard authUser={authUser} {...props} />
)}
</AuthUserContext.Consumer>
)} />
и удалил потребителя из оператора рендеринга компонента панели мониторинга.
Затем в componentDidMount на компоненте Dashboard я попытался:
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,
});
}
Когда я пытаюсь это сделать, я получаю сообщение об ошибке:
FirebaseError: Функция CollectionReference.doc () требует, чтобы ее первый аргумент имел тип непустой строки, но это был: пользовательский объект DocumentReference
СЛЕДУЮЩАЯ ПОПЫТКА Представленные ниже люди, кажется, находят что-то полезное в первом предложенном решении. Я не смог найти в нем ничего полезного, но, читая его предложения, я изо всех сил пытаюсь понять, как работает пример в документации по Firebase (он не раскрывает, как дать значение: uid для запроса .doc (). ), который выглядит следующим образом:
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!");
}
принципиально отличается от моей попытки в функции componentDidMount, которая:
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");
}
);
}
Возможно, решение этого шага - ключ, который поможет приблизить это к результату. Может ли кто-нибудь найти лучшую документацию по пожарному хранилищу, чтобы показать, как получить запись коллекции пользователей, используя зарегистрированный пользовательский uid слушателя?
С этой целью я вижу из примера лаборатории кода FriendlyEats , что делается попытка присвоить doc.id значение поиска id в коде. Я не знаю, на каком языке написан этот код - но он действительно похож на то, что я пытаюсь сделать - я просто не понимаю, как перейти от этого примера к тому, с чем я знаю, как работать.
display: function(doc) {
var data = doc.data();
data['.id'] = doc.id;
data['go_to_restaurant'] = function() {
that.router.navigate('/restaurants/' + doc.id);
};
Ответы:
Из последней строки вашего вопроса (
users = () => this.db.collection('users');
) я понимаю, что вызывается коллекция, в которой вы храните дополнительную информацию о пользователях,users
и что пользовательский документ в этой коллекции использует userId (uid) в качестве docId.Следующее должно сделать трюк (не проверено):
Таким образом, в наблюдателе, установленном с помощью
onAuthStateChanged()
метода, когда мы обнаруживаем, что пользователь вошел в систему (т.е. вif (authUser) {}
), мы используем егоuid
для запроса уникального документа, соответствующего этому пользователю вusers
коллекции (см. Чтение одного документа и документ документаget()
метод).источник
this.auth.onAuthStateChanged(authUser => { if (authUser) {console.log(authUser.uid) })
?У меня есть теория, которую я бы хотел проверить.
Я думаю, что когда вы вызываете
next(authUser)
внутри вашегоonAuthStateChanged
обработчика, во время его выполнения он сталкивается с ошибкой (например,cannot read property 'name' of undefined at ...
).Причина, по которой ваш код не работает должным образом, заключается в том, что там, где вы звоните
next(authUser)
, он находится внутриthen()
цепочки Promise. Любые ошибки, возникшие в Обещании, будут обнаружены и приведут к отклонению Обещания. Когда Promise отклоняется, он вызывает вызывающие ошибки обработчики ошибок. В рассматриваемой цепочке Promise такого обработчика ошибок нет.Если я потерял вас, прочитайте это сообщение в блоге для ускоренного курса Promises, а затем возвращайтесь.
Итак, что мы можем сделать, чтобы избежать такой ситуации? Простейшим будет вызов
next(authUser)
за пределами области действияthen()
обработчика Promise . Мы можем сделать это, используяwindow.setTimeout(function)
.Так что в вашем коде вы бы заменили
с
Это приведет к возникновению любых ошибок, как обычно, а не к цепочке обещаний.
Важно отметить, что у вас нет обработчика catch, который обрабатывает
userDocRef.get()
ошибки. Так что просто добавьте.catch(() => setTimeout(fallback))
в конецthen()
так, чтобы ваш код использовал резервный метод, если он выдает ошибку.Итак, мы заканчиваем с:
Отредактированный код
Вышеприведенное объяснение должно позволить вам исправить ваш код, но вот моя версия вашего
Firebase
класса с различными изменениями простоты использования.Применение:
Определение класса:
Обратите внимание, что в этой версии
onUserDataListener
метод возвращает функцию отказа от подпискиonAuthStateChanged
. Когда ваш компонент размонтирован, вы должны отсоединить все соответствующие прослушиватели, чтобы не было утечки памяти или неработающего кода, работающего в фоновом режиме, когда он не нужен.источник
console.log(snapshot.data())
?В вашей
AuthContext.Provider
реализации вы получаете доступ кonAuthStateChanged
слушателю SDK напрямую:Это должно быть изменено для использования
onAuthUserListener
из вашего вспомогательного класса:Что касается сообщений журнала, заполненных множеством случайных свойств, это связано с тем, что
firebase.User
объект имеет как открытый API, так и реализацию с рядом частных свойств и методов, которые минимизируются при компиляции. Поскольку эти минимизированные свойства и методы явно не помечены как не перечисляемые, они включаются в любой вывод журнала. Если вместо этого вы хотите регистрировать только те части, которые действительно полезны, вы можете деструктурировать и реструктурировать объект, используя:источник
Dashboard
файлаexport default withAuthentication(Dashboard);
(а неwithFirebase
)Если кто-то еще застрял подобным образом, я нашел решение здесь: Firebase & React: CollectionReference.doc () тип аргумента
Он не работает при обновлении страницы (по-прежнему выдает ошибку, что uid имеет значение null), но реагирует на перехваты для использования useEffect, если функция componentDidMount заменена комбинацией Mount и Update. Я пытаюсь это дальше.
источник