【发布时间】:2020-04-26 16:15:14
【问题描述】:
我试图弄清楚如何获取用户名,该用户名是存储在用户集合中的属性,该属性已与 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 属性与用户集合属性合并,如下所示:
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(它可以让我使用 Authentication 属性)获取到用户集合的方法,该集合的 id 与 Authentication 集合中的 uid 相同。
我见过this post,它似乎有同样的问题,并试图找出答案应该暗示的内容 - 但我似乎无法找到一种可以从身份验证集合中获取的方法到用户集合,如果合并没有让我从 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: 无法读取 null 的属性“电子邮件”
更新: 我刚刚意识到,在控制台日志中,我必须展开一个标记为的下拉菜单:
Q {I: Array(0), l:
查看电子邮件属性。有谁知道这个乱七八糟的暗示是什么?我找不到密钥来弄清楚 Q、I 或 l 的含义,以了解我是否应该引用这些东西来获取 Authentication 表中的相关属性。也许如果我能弄清楚 - 我可以找到一种使用身份验证集合中的 uid 访问用户集合的方法。
有没有人在前端使用 react,通过上下文消费者来找出当前用户是谁?如果是这样,您如何访问它们在 Authentication 模型上的属性以及您如何访问相关 User 集合上的属性(其中 User 文档上的 docId 是 Authentication 表中的 uid)?
下一次尝试
下一次尝试产生了一个非常奇怪的结果。
我有 2 个单独的页面作为上下文消费者。它们的区别在于一个是函数,另一个是类组件。
在函数组件中,我可以渲染 {authUser.email}。当我尝试在类组件中做同样的事情时,我收到一条错误消息:
TypeError: 无法读取 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;
虽然我无法访问用户文档上的 docId 与经过身份验证的用户的 uid 相同的用户集合属性,但从这个组件中,我可以输出该用户的身份验证集合上的电子邮件属性。
虽然Firebase documentation 在这里提供了管理用户和访问属性的建议,但我还没有找到在 react 中实现这种方法的方法。通过在我的 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 的值作为“未定义”返回,其中 db 中有一个字符串。
此尝试:
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-expressions
当我尝试时:
{
this.props.firebase.db.collection('users').doc(authUser.uid).get("name")
.then(doc => {
console.log(doc.data().name),
<p>doc.data().name</p>
})
}
错误信息说:
期待一个赋值或函数调用,却看到了一个表达式
我已经准备好放弃尝试找出如何让快照查询工作的尝试——如果我可以让用户集合的名称呈现在屏幕上。任何人都可以帮助完成这一步吗?
下一次尝试
我找到了this post。它很好地解释了需要发生的事情,但我无法如图所示实现它,因为 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>
)} />
并从仪表板组件渲染语句中删除消费者。
然后在 Dashboard 组件上的 componentDidMount 中,我尝试了:
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 文档中的示例(它没有披露如何为 .doc() 请求提供 :uid 值),如下:
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");
}
);
}
也许解决这一步是有助于将其推向结果的线索。谁能找到更好的 firestore 文档来展示如何使用登录的用户侦听器 uid 获取用户收集记录?
为此,我可以从 FriendlyEats 代码实验室example 看到,有人尝试将 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);
};
【问题讨论】:
-
仅供参考,您的术语不太正确,使这个问题难以阅读。 Firebase 中没有任何东西称为“表”。在 Firebase Auth 中,只有用户 - 没有“身份验证表”。在 Firestore 中,有集合和这些集合中的文档,但没有表。我试图弄清楚你在哪里卡住了,以及你显示的代码如何没有按照你期望的方式工作,但我只是没有把它们拼凑在一起。考虑编辑问题以使用您可以在文档中找到的更标准的术语,并更清楚地了解哪些内容没有按您的预期工作。
-
很好 - 很乐意用表格代替集合。重点还是一样。
-
我的主要观点是我无法真正理解您的观点,而且术语也没有帮助。您能否更详细地解释一下您显示的代码中哪些不起作用?有什么事情没有按预期工作吗?有任何错误或调试日志来说明吗?
-
没有任何效果。我期待找到一种从 authUser 侦听器访问用户集合详细信息的方法。 authUser 在上下文处理程序和相关的类方法中定义,用于侦听方法的更改。我无法通过身份验证集合中的属性 - 我正在尝试访问 firestore 中的相关用户集合。没有日志。只是指出该字段未定义的错误消息。
-
我建议从一个简单的任务开始,让它发挥作用以获得一些经验,然后将其应用于更复杂的问题,比如你现在遇到的问题。文档没有什么根本不正确的地方(我知道,因为我一直在使用它,而且我也帮助人们)。为了在 Stack Overflow 上获得帮助,您需要说明一个特定的问题,最好是一个任何人都可以用来重现该问题的 MCVE。仅仅说“我无法让它工作”是不够的。 stackoverflow.com/help/minimal-reproducible-example
标签: reactjs firebase google-cloud-firestore firebase-authentication