React中从Firestore获取当前用户数据失败的问题求助
我搭了一套注册/认证系统,注册时Firestore的users集合能正确存入name、bio、email、imgURL、uid等用户信息,但在React的portfolio页面获取当前登录用户详情时,返回值始终是undefined或null。试过直接用uid查文档、onAuthStateChanged监听、获取整个users集合(只能拿到所有用户,筛不出当前用户),都没解决。
相关代码片段
continueSignUp.jsx(存入用户信息到Firestore)
const ContinueSignUp = () => { const [file, setFile] = useState(); const [url, setURL] = useState(); const [name, setName] = useState(''); const [bio, setBio] = useState(''); let user = firebase.auth().currentUser; let uid = user.uid; let email = user.email; const hidePlaceholder = url ? { display: "none" } : {}; const navigate = useNavigate(); function handleChange(e) { if (e.target.files && e.target.files[0]) setFile(e.target.files[0]); setURL(URL.createObjectURL(e.target.files[0])); } async function handleUpload(e) { e.preventDefault(); const path = `/images/${file.name}`; const ref = storage.ref(path); await ref.put(file); const url = await ref.getDownloadURL(); setURL(url); setFile(null); console.log(file); if (name === "") { return; } const userRef = collection(db, "users"); addDoc(userRef, { name, bio, url, uid, email }) .then((response) => { console.log(response); navigate("/portfolio") }) .catch((error) => console.log(error.message)); } }
portfolio.jsx(尝试获取用户信息)
const Portfolio = () => { const uid = firebase.auth().currentUser.uid; const currentUserProfile = firebase.firestore().collection("users").doc(uid); useEffect(() => { currentUserProfile.get().then((doc) => { const userName = doc.data().bio; console.log(uid); }).catch((err) => {console.log("Error in displaying Name!")}) }, []); }
*注:实际文档ID已替换为0
尝试获取全集合的代码
const [profile, setProfile] = useState(null); useEffect(() => { getUserData(); }, []); useEffect(() => { console.log(profile); }, [profile]); function getUserData() { const userRef = collection(db, "users"); getDocs(userRef) .then((response) => { const user = response.docs.map((doc) => ({ data: doc.data(), id: doc.id, })); console.log(response); setProfile(user); }) .catch((error) => console.log(error.message)); }
console.log(profile)输出:
[{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}] 0: {data: {…}, id: '00000000000000000000'} 1: {data: {…}, id: '00000000000000000000'} 2: {data: {…}, id: '00000000000000000000'} 3: {data: {…}, id: '00000000000000000000'} 4: {data: {…}, id: '00000000000000000000'} 5: {data: {…}, id: '00000000000000000000'} 6: {data: {…}, id: '00000000000000000000'} 7: {data: {…}, id: '00000000000000000000'} 8: {data: {…}, id: '00000000000000000000'}
问题原因与解决方法
核心问题:文档ID不匹配
你用addDoc存入用户数据时,Firestore会自动生成随机文档ID,但在portfolio页面用doc(uid)查询时,是用用户uid作为文档ID查找的——两者完全不匹配,自然查不到数据。
解决步骤
修改存入逻辑:用用户uid作为文档ID
把addDoc改成setDoc,指定文档ID为用户uid,后续查询就能直接通过uid定位文档:// 替换continueSignUp里的addDoc部分 const userDocRef = doc(db, "users", uid); // 用uid作为文档ID setDoc(userDocRef, { name, bio, url, uid, email }) .then((response) => { console.log(response); navigate("/portfolio") }) .catch((error) => console.log(error.message));修复portfolio页面的查询逻辑
直接在组件顶层取firebase.auth().currentUser可能为null(auth还没初始化完成),必须用onAuthStateChanged监听用户状态:const Portfolio = () => { const [userProfile, setUserProfile] = useState(null); const navigate = useNavigate(); useEffect(() => { const unsubscribe = firebase.auth().onAuthStateChanged(async (currentUser) => { if (currentUser) { // 用户已登录,查询对应文档 const userDocRef = doc(db, "users", currentUser.uid); const docSnapshot = await getDoc(userDocRef); if (docSnapshot.exists()) { setUserProfile(docSnapshot.data()); console.log(docSnapshot.data()); } else { console.log("用户文档不存在"); } } else { // 用户未登录,跳转到登录页 navigate("/login"); } }); // 组件卸载时取消监听,避免内存泄漏 return () => unsubscribe(); }, [navigate]); return ( <div> {userProfile ? ( <div> <h1>{userProfile.name}</h1> <p>{userProfile.bio}</p> <img src={userProfile.url} alt="Profile" /> </div> ) : ( <p>加载中...</p> )} </div> ); };
备用方案:不修改现有文档结构
如果暂时不想改已有的文档结构,可以通过where条件筛选uid匹配的文档:
// 在portfolio页面中用where查询 const querySnapshot = await getDocs(query(collection(db, "users"), where("uid", "==", currentUser.uid))); if (!querySnapshot.empty) { const userData = querySnapshot.docs[0].data(); setUserProfile(userData); }
代码优化建议
封装Firebase操作
把Firestore、Auth的操作封装成单独的工具函数,比如firebase/utils.js:export const saveUserProfile = async (uid, profileData) => { const userDocRef = doc(db, "users", uid); await setDoc(userDocRef, profileData); }; export const getUserProfile = async (uid) => { const userDocRef = doc(db, "users", uid); const docSnapshot = await getDoc(userDocRef); return docSnapshot.exists() ? docSnapshot.data() : null; };组件代码会更简洁,也方便后续维护。
处理加载与错误状态
在组件中添加加载状态和错误状态,提升用户体验:const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const unsubscribe = firebase.auth().onAuthStateChanged(async (currentUser) => { setLoading(true); try { if (currentUser) { const profile = await getUserProfile(currentUser.uid); setUserProfile(profile); } else { navigate("/login"); } } catch (err) { setError(err.message); } finally { setLoading(false); } }); return () => unsubscribe(); }, [navigate]); // 渲染时处理状态 if (loading) return <p>加载中...</p>; if (error) return <p>出错了:{error}</p>;表单验证优化
在handleUpload中,除了判断name为空,还可以添加更多验证逻辑(比如bio长度、文件类型),同时给用户明确提示,而不是直接return。
内容的提问来源于stack exchange,提问作者user21317313

