我第一次用钩子测试Firebase,遇到了众所周知的无限循环问题。
我知道还有许多其他问题可能接近这个问题,但我仍然无法在这种情况下解决这个问题。
这是应用程序上的代码.js:
import React, { useState, useEffect } from 'react';
import { auth, createUserProfileDocument } from './firebase/firebase.utils';
function App() {
const [user, setUser] = useState(null);
useEffect(() => {
auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
userRef.onSnapshot(snapshot => {
setUser({
user: {
id: snapshot.id,
...snapshot.data()
}
})
});
console.log(user);
} else {
setUser(null);
}
});
}, [user]);
return (
<div></div>
);
}
export default App;
这里是 firebase.utils.js 的 createUserProfileDocument 函数:
export const createUserProfileDocument = async (userAuth, additionalData) => {
if (!userAuth) return;
const userRef = firestore.doc(`users/${userAuth.uid}`);
const snapshot = await userRef.get();
if (!snapshot.exists) {
const { displayName, email } = userAuth;
const createdAt = new Date();
try {
await userRef.set({
displayName,
email,
createdAt,
...additionalData
})
} catch (err) {
console.log('Error creating user', err.message);
}
}
return userRef;
};
在这种情况下,我将如何检查onAuthStateChanged是否确实发生了变化?我的印象是每次都会触发auth.onAuthStateChanged函数,生成无限循环。
以下是我检查的资源可能会有所帮助:
中等
堆栈溢出
提前谢谢你。
该方法onAuthStateChanged
通过为用户的登录状态添加观察者来设置订阅。只需在组件挂载时订阅一次,在卸载组件时调用取消订阅,以防止观察器运行并导致内存泄漏。
为了回答为什么它会导致无限循环,您将user
状态作为依赖项,这将导致观察者在每次更新状态值user
时重新初始化,观察者将返回一个全新的userAuth
对象并再次调用setUser
它将更新用户状态,这将重新初始化观察器并一遍又一遍地重复相同的循环。
解决方案,
仅当组件装载时,通过将空数组作为依赖项传递给useEffect
来设置订阅一次
function App() {
const [user, setUser] = useState(null);
useEffect(() => {
// auth.onAuthStateChanged will return a firebase.Unsubrcibe function
// which you can call to terminate the subscription
const unsubscribe = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
userRef.onSnapshot(snapshot => {
setUser({
user: {
id: snapshot.id,
...snapshot.data()
}
})
});
} else {
setUser(null);
}
});
// return a clean up function that will call unsubscribe to -
// terminate the subscription when component unmounts
return () => { unsubscribe() }
}, []); // important set an empty array as dependency
return (
<div></div>
);
}
export default App;
编辑 - 不要尝试将user
变量记录在同一个useEffect
钩子中,因为它需要一个空的依赖数组,而是使用另一个useEffect
钩子来记录user
将user
作为依赖项传递的值。例
useEffect(() => {
console.log(user)
}, [user])