在 AuthStateChanged 上反应本机 firebase
react native firebase onAuthStateChanged
在我使用 firebase 作为后端的 react-native 应用程序中,我有一个像这样的初始 LoginScreen:
(react-navigation 的导航函数呈现屏幕作为第一个参数传递作为 props.navigation.params.state 第二个参数传递给该屏幕)
class LoginScreen extends Component {
constructor() {
this.state = {
currentUser: {},
email: null,
psw: null
...
async logIn(email, pass) {
try {
await firebaseApp.auth()
.signInWithEmailAndPassword(email, pass)
} catch (error) {
alert(error.toString())
}
}
listenForAuth() {
const { navigation } = this.props
this.unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
this.setState({
currentUser: {
id: user.uid
}
})
navigation.navigate('MainApp', { currentUser: this.state.currentUser })
} else {
this.setState({
loading: false
})
}
})
}
componentWillMount() {
this.listenForAuth()
}
render() {
...
<LoginButton onPress={() => this.logIn(this.state.email,this.state.psw)
...
和 MainApp 组件作为反应导航 tabNavigator,屏幕 'Profile' 看起来像:
class Profile extends Component {
constructor(props) {
super(props)
const { currentUser } = this.props.navigation.state.params
this.userRef = firebaseApp.database().ref( 'users/' + currentUser.id )
this.postsRef = firebaseApp.database().ref( 'users/' + currentUser.id + '/myposts' )
this.state = {
currentUser: {},
refreshing: false,
postsDataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
})
}
...
listenForUser(usersRef) {
usersRef.on('value', snap => {
this.setState({
currentUser: {
user: snap.val().user,
photo: snap.val().photo,
email: snap.val().email,
id: snap.val().id
}
})
})
listenForPosts(postsRef) {
postsRef.on('value', snap => {
var posts = []
snap.forEach( trip => {
firebaseApp.database().ref( 'posts/' + posts.key ).on('value', child => {
posts.push({
title: child.val().title,
own: child.val().own,
_key: child.key
})
})
})
this.setState({
postsDataSource: this.state.postsDataSource.cloneWithRows(posts)
})
})
}
componentDidMount() {
this.listenForUser(this.userRef)
this.listenForPosts(this.postsRef)
}
_onRefresh() {
this.setState({ refreshing: true })
this.listenForPosts(this.postsRef)
this.setState({ refreshing: false })
}
...
render() {
...
<Text>{this.state.currentUser.user}</Text>
...
<ListView dataSource={this.state.postsDataSource}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._onRefresh.bind(this)}
/>}
...
当应用程序启动时,侦听器第一次感知到用户的存在,它导航到 MainApp 屏幕但出现警告,组件的渲染函数中的 {this.state.currentUser.user}配置文件未定义;但是当我浏览 tabBar 时,我可以看到该用户名,但 ListView 是空的。如果我刷新,将检索 firebase 数据并填充 ListView。
如果我注销并重新登录不再发生,数据会立即加载,不会出现警告并会立即填充列表视图。
如果我退出并重新启动应用程序,因为已经有一个登录用户,它会导航到 MainApp 屏幕,但如果我注销并重新登录,一切正常,就会出现上面看到的相同问题。
拜托,我的错误是什么?
有人可以帮助我吗?
问题是加载onAuthStateChanged时,currentUser还是null。但是,如果您稍等片刻,稍后再试(例如,相同的代码,但在另一个屏幕上),一切都会正常。这就是为什么你第二次尝试没有错误。
我使用 setInterval 函数作为旁路——它将自行执行,直到它检索到 currentUser 对象
let renderAction = setInterval(() => {
if ( firebase.auth().currentUser !== null ) {
clearInterval(renderAction);
let user = firebase.auth().currentUser;
let uid = user.uid;
this.setState({uid});
} else {
console.log('Wait for it');
}
}, 500);
另请注意,获取currentUser.uid不是一个好主意,应该先等待currentUser,然后再获取其属性。希望你没有等太久,已经找到了解决办法。
在我使用 firebase 作为后端的 react-native 应用程序中,我有一个像这样的初始 LoginScreen: (react-navigation 的导航函数呈现屏幕作为第一个参数传递作为 props.navigation.params.state 第二个参数传递给该屏幕)
class LoginScreen extends Component {
constructor() {
this.state = {
currentUser: {},
email: null,
psw: null
...
async logIn(email, pass) {
try {
await firebaseApp.auth()
.signInWithEmailAndPassword(email, pass)
} catch (error) {
alert(error.toString())
}
}
listenForAuth() {
const { navigation } = this.props
this.unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
this.setState({
currentUser: {
id: user.uid
}
})
navigation.navigate('MainApp', { currentUser: this.state.currentUser })
} else {
this.setState({
loading: false
})
}
})
}
componentWillMount() {
this.listenForAuth()
}
render() {
...
<LoginButton onPress={() => this.logIn(this.state.email,this.state.psw)
...
和 MainApp 组件作为反应导航 tabNavigator,屏幕 'Profile' 看起来像:
class Profile extends Component {
constructor(props) {
super(props)
const { currentUser } = this.props.navigation.state.params
this.userRef = firebaseApp.database().ref( 'users/' + currentUser.id )
this.postsRef = firebaseApp.database().ref( 'users/' + currentUser.id + '/myposts' )
this.state = {
currentUser: {},
refreshing: false,
postsDataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
})
}
...
listenForUser(usersRef) {
usersRef.on('value', snap => {
this.setState({
currentUser: {
user: snap.val().user,
photo: snap.val().photo,
email: snap.val().email,
id: snap.val().id
}
})
})
listenForPosts(postsRef) {
postsRef.on('value', snap => {
var posts = []
snap.forEach( trip => {
firebaseApp.database().ref( 'posts/' + posts.key ).on('value', child => {
posts.push({
title: child.val().title,
own: child.val().own,
_key: child.key
})
})
})
this.setState({
postsDataSource: this.state.postsDataSource.cloneWithRows(posts)
})
})
}
componentDidMount() {
this.listenForUser(this.userRef)
this.listenForPosts(this.postsRef)
}
_onRefresh() {
this.setState({ refreshing: true })
this.listenForPosts(this.postsRef)
this.setState({ refreshing: false })
}
...
render() {
...
<Text>{this.state.currentUser.user}</Text>
...
<ListView dataSource={this.state.postsDataSource}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._onRefresh.bind(this)}
/>}
...
当应用程序启动时,侦听器第一次感知到用户的存在,它导航到 MainApp 屏幕但出现警告,组件的渲染函数中的 {this.state.currentUser.user}配置文件未定义;但是当我浏览 tabBar 时,我可以看到该用户名,但 ListView 是空的。如果我刷新,将检索 firebase 数据并填充 ListView。
如果我注销并重新登录不再发生,数据会立即加载,不会出现警告并会立即填充列表视图。
如果我退出并重新启动应用程序,因为已经有一个登录用户,它会导航到 MainApp 屏幕,但如果我注销并重新登录,一切正常,就会出现上面看到的相同问题。
拜托,我的错误是什么? 有人可以帮助我吗?
问题是加载onAuthStateChanged时,currentUser还是null。但是,如果您稍等片刻,稍后再试(例如,相同的代码,但在另一个屏幕上),一切都会正常。这就是为什么你第二次尝试没有错误。
我使用 setInterval 函数作为旁路——它将自行执行,直到它检索到 currentUser 对象
let renderAction = setInterval(() => {
if ( firebase.auth().currentUser !== null ) {
clearInterval(renderAction);
let user = firebase.auth().currentUser;
let uid = user.uid;
this.setState({uid});
} else {
console.log('Wait for it');
}
}, 500);
另请注意,获取currentUser.uid不是一个好主意,应该先等待currentUser,然后再获取其属性。希望你没有等太久,已经找到了解决办法。