Firestore中获取当前用户专属Profile UID的技术问题
问题描述
我正在开发React+Firestore项目,数据库中有2000个不同用户的Profile UID。当前代码会获取所有用户的UID,但我需要仅获取当前登录用户创建的特定Profile UID,而非全部用户的UID。
相关代码及截图
Sidebar.js
import React, { useEffect, useState } from "react"; import { Link } from 'react-router-dom'; import { Card, Typography, List, ListItem, ListItemPrefix, ListItemSuffix, Chip, Input, } from "@material-tailwind/react"; import { UserCircleIcon, InboxIcon, } from "@heroicons/react/solid"; import { SearchIcon } from "@heroicons/react/outline"; import { db } from '../firebase/firebaseConfig'; import { collection, getDocs } from 'firebase/firestore'; import { getAuth } from "firebase/auth"; const Sidebar = () => { const [userProfiles, setUserProfiles] = useState([]); const auth = getAuth(); useEffect(() => { const fetchUserProfiles = async () => { try { const profileCollectionRef = collection(db, 'profiles'); const querySnapshot = await getDocs(profileCollectionRef); const profilesData = []; querySnapshot.forEach((doc) => { profilesData.push({ id: doc.id, ...doc.data() }); }); setUserProfiles(profilesData); } catch (error) { console.error('Error fetching user profiles:', error); } }; fetchUserProfiles(); }, []); return ( <Card className="h-[calc(100vh-2rem)] w-full max-w-[20rem] p-4 shadow-xl shadow-blue-gray-900/5 text-gray-500"> <div className="mb-2 flex items-center gap-4 p-4 font-bricolage-grotesque"> <img src="Logo.svg" alt="brand" className="h-8 w-8" /> <Typography variant="h5" color="blue-gray"> Sidebar </Typography> </div> <div className="p-2"> <Input className="rounded-md w-[16.6rem]" icon={<SearchIcon className="h-5 focus:none pt-2 pl-2 w-5" />} /> </div> <List> <hr className="my-2 border-blue-gray-50" /> <Link to="/Offers" className="ml-1 no-underline text-black"> <ListItem> <ListItemPrefix> <InboxIcon className="h-5 w-5" /> </ListItemPrefix> <p className="ml-1 text-black font-gowun-dodum text-[1.5rem] ">Offers</p> <ListItemSuffix> <Chip value="14" size="sm" variant="ghost" color="blue-gray" className="rounded-full ml-1" /> </ListItemSuffix> </ListItem> </Link> {userProfiles.map((profile) => ( <Link key={profile.id} to={`/Profile/${profile.id}` || '/CreateProfile'} className="ml-1 mt-4 no-underline text-black"> <ListItem> <ListItemPrefix> <UserCircleIcon className="h-5 w-5" /> </ListItemPrefix> <button className="font-gowun-dodum text-[1.5rem] bg-white text-black">{"Your Profile" || "CreateProfile"}</button> </ListItem> </Link> ))} <Link to="/Settings" className="ml-1 no-underline text-black"> <ListItem> <ListItemPrefix> <img src="cog.svg" className="h-5 w-5" /> </ListItemPrefix> <p className=" font-gowun-dodum text-[1.5rem] ml-1 text-black">Settings</p> </ListItem> </Link> </List> </Card> ); } export default Sidebar;
CreateProfile.js
import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { db } from '../firebase/firebaseConfig'; import { doc, getDoc, setDoc } from 'firebase/firestore'; import { v4 as uuidv4 } from 'uuid'; const defaultProfileData = { name: '', about: '', previousOccupation: '', email: '', social: '', portfolio_website: '', githublink: '', }; export default function CreateProfile() { const navigate = useNavigate(); const [profileData, setProfileData] = useState(defaultProfileData); const [profileExists, setProfileExists] = useState(false); useEffect(() => { const userName = uuidv4(); const checkProfile = async () => { try { const profileRef = doc(db, 'profiles', userName); const profileSnapshot = await getDoc(profileRef); if (profileSnapshot.exists()) { setProfileExists(true); } } catch (error) { console.error('Error checking user profile:', error); } }; checkProfile(); }, []); const handleSubmit = async (e) => { e.preventDefault(); try { const userName = uuidv4(); const profileRef = doc(db, 'profiles', userName); await setDoc(profileRef, profileData); console.log('User profile created successfully.'); navigate(`/profile/${userName}`); } catch (error) { console.error('Error creating user profile:', error); } }; if (profileExists) { return null; } return ( <div className='w-[24rem] mx-auto'> <h1 className='text-2xl font-semibold mb-4'>Create Your Profile</h1> <form onSubmit={handleSubmit}> {/* Name */} <div className='mb-4'> <label htmlFor='name' className='block text-gray-700 font-medium mb-2'> Your Name </label> <input type='text' id='name' className='w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring focus:border-blue-300' value={profileData.name} onChange={(e) => setProfileData({ ...profileData, name: e.target.value })} maxLength={30} /> </div> {/* About Me */} <div className='mb-4'> <label htmlFor='about' className='block text-gray-700 font-medium mb-2'> About Me </label> <textarea id='about' className='w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring focus:border-blue-300' rows='4' value={profileData.about} onChange={(e) => setProfileData({ ...profileData, about: e.target.value })} maxLength={200} /> </div> {/* Previous Occupation */} <div className='mb-4'> <label htmlFor='previousOccupation' className='block text-gray-700 font-medium mb-2'> Previous Occupation </label> <input maxLength={50} type='text' id='previousOccupation' className='w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring focus:border-blue-300' value={profileData.previousOccupation} onChange={(e) => setProfileData({ ...profileData, previousOccupation: e.target.value })} /> </div> {/* Email */} <div className='mb-4'> <label htmlFor='email' className='block text-gray-700 font-medium mb-2'> Email </label> <input maxLength={50} type='email' id='email' className='w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring focus:border-blue-300' value={profileData.email} onChange={(e) => setProfileData({ ...profileData, email: e.target.value })} /> </div> {/* Social Links */} <div className='mb-4'> <label htmlFor='social' className='block text-gray-700 font-medium mb-2'> Social Links </label> <input maxLength={80} type='text' id='social' className='w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring focus:border-blue-300' value={profileData.social} onChange={(e) => setProfileData({ ...profileData, social: e.target.value })} /> </div> <div className='mb-4'> <label htmlFor='portfolio_website' className='block text-gray-700 font-medium mb-2'> Portfolio Website </label> <input maxLength={80} type='text' id='portfolio_website' className='w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring focus:border-blue-300' value={profileData.portfolio_website} onChange={(e) => setProfileData({ ...profileData, portfolio_website: e.target.value })} /> </div> <div className='mb-4'> <label htmlFor='portfolio_website' className='block text-gray-700 font-medium mb-2'> Github Link </label> <input maxLength={80} type='text' id='githublink' className='w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring focus:border-blue-300' value={profileData.githublink} onChange={(e) => setProfileData({ ...profileData, githublink: e.target.value })} /> </div> <button type='submit' className='w-full py-2 bg-blue-500 text-white font-semibold rounded-lg hover:bg-blue-600' > Create Profile </button> </form> </div> ); }
Firebase规则

数据库截图

当前结果截图

解决方案
核心问题是代码未将用户创建的Profile与登录用户UID关联,且查询时无过滤条件。以下是修复步骤:
1. 关联Profile与登录用户UID
修改CreateProfile.js,用当前登录用户的UID作为Profile文档ID(保证每个用户仅能创建一个Profile),并将用户UID存入文档:
import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { db } from '../firebase/firebaseConfig'; import { doc, getDoc, setDoc } from 'firebase/firestore'; import { getAuth } from "firebase/auth"; const defaultProfileData = { name: '', about: '', previousOccupation: '', email: '', social: '', portfolio_website: '', githublink: '', }; export default function CreateProfile() { const navigate = useNavigate(); const [profileData, setProfileData] = useState(defaultProfileData); const [profileExists, setProfileExists] = useState(false); const auth = getAuth(); const currentUser = auth.currentUser; useEffect(() => { const checkProfile = async () => { if (!currentUser) return; try { const profileRef = doc(db, 'profiles', currentUser.uid); const profileSnapshot = await getDoc(profileRef); if (profileSnapshot.exists()) { setProfileExists(true); navigate(`/profile/${currentUser.uid}`); } } catch (error) { console.error('Error checking user profile:', error); } }; checkProfile(); }, [currentUser, navigate]); const handleSubmit = async (e) => { e.preventDefault(); if (!currentUser) return; try { const profileRef = doc(db, 'profiles', currentUser.uid); await setDoc(profileRef, { ...profileData, createdBy: currentUser.uid }); console.log('User profile created successfully.'); navigate(`/profile/${currentUser.uid}`); } catch (error) { console.error('Error creating user profile:', error); } }; if (profileExists || !currentUser) { return null; } return ( <div className='w-[24rem] mx-auto'> <h1 className='text-2xl font-semibold mb-4'>Create Your Profile</h1> <form onSubmit={handleSubmit}> {/* 原表单字段保持不变 */} </form> </div> ); }
2. 修改Sidebar查询逻辑,仅获取当前用户的Profile
修改Sidebar.js,监听用户登录状态,仅查询当前用户的Profile:
import React, { useEffect, useState } from "react"; import { Link } from 'react-router-dom'; import { Card, Typography, List, ListItem, ListItemPrefix, ListItemSuffix, Chip, Input, } from "@material-tailwind/react"; import { UserCircleIcon, InboxIcon, } from "@heroicons/react/solid"; import { SearchIcon } from "@heroicons/react/outline"; import { db } from '../firebase/firebaseConfig'; import { collection, query, where, getDocs, doc, getDoc } from 'firebase/firestore'; import { getAuth, onAuthStateChanged } from "firebase/auth"; const Sidebar = () => { const [userProfiles, setUserProfiles] = useState([]); const auth = getAuth(); const [currentUser, setCurrentUser] = useState(null); useEffect(() => { const unsubscribe = onAuthStateChanged(auth, (user) => { setCurrentUser(user); if (user) { fetchUserProfile(user); } else { setUserProfiles([]); } }); return () => unsubscribe(); }, [auth]); const fetchUserProfile = async (user) => { try { // 方式一:用用户UID直接获取单个文档 const profileRef = doc(db, 'profiles', user.uid); const profileSnapshot = await getDoc(profileRef); if (profileSnapshot.exists()) { setUserProfiles([{ id: profileSnapshot.id, ...profileSnapshot.data() }]); } else { setUserProfiles([]); } // 方式二:如果用createdBy字段过滤(备用) // const profileQuery = query( // collection(db, 'profiles'), // where('createdBy', '==', user.uid) // ); // const querySnapshot = await getDocs(profileQuery); // const profilesData = []; // querySnapshot.forEach((doc) => profilesData.push({ id: doc.id, ...doc.data() })); // setUserProfiles(profilesData); } catch (error) { console.error('Error fetching user profile:', error); } }; return ( <Card className="h-[calc(100vh-2rem)] w-full max-w-[20rem] p-4 shadow-xl shadow-blue-gray-900/5 text-gray-500"> <div className="mb-2 flex items-center gap-4 p-4 font-bricolage-grotesque"> <img src="Logo.svg" alt="brand" className="h-8 w-8" /> <Typography variant="h5" color="blue-gray"> Sidebar </Typography> </div> <div className="p-2"> <Input className="rounded-md w-[16.6rem]" icon={<SearchIcon className="h-5 focus:none pt-2 pl-2 w-5" />} /> </div> <List> <hr className="my-2 border-blue-gray-50" /> <Link to="/Offers" className="ml-1 no-underline text-black"> <ListItem> <ListItemPrefix> <InboxIcon className="h-5 w-5" /> </ListItemPrefix> <p className="ml-1 text-black font-gowun-dodum text-[
相关产品推荐
相关产品推荐

