React+Firebase:上传新数据后自动覆盖Firestore旧数据方案问询
在React中使用Firebase实现头像更新时自动覆盖旧数据
需求背景
当用户更新个人头像时,需要在Firestore中直接覆盖旧的头像数据,避免产生重复记录。当前使用Firebase Storage存储图片文件,将图片的下载URL存入Firestore数据库,但之前通过前端过滤多用户数据的方式导致产生大量重复数据。
现有实现代码
const [userImg, setUserImg] = useState() const [image, setImage] = useState(null) const [htlmImg, setHtmlImg] = useState(null) const [url, setUrl] = useState(null) const [userName, setUserName] = useState(null) const [sureLoading, setSureLoading] = useState(false) const [photoEdit, setPhotoEdit] = useState(false) const handleImageChange = (e) => { if (e.target.files[0]) { setImage(e.target.files[0]) setHtmlImg(URL.createObjectURL(e.target.files[0])) } } const uploadImg = () => { const imageRef = ref(storage, `image${user.uid}`) uploadBytes(imageRef, image) .then(() => { getDownloadURL(imageRef) .then((url) => { setUrl(url) }) .catch((error) => { console.log(error.message, 'error getting the image url') }) setImage(null) }) .catch((error) => { console.log(error.message) }) setSureLoading(true) } const handlePfpSubmit = async () => { const { uid } = user if (url !== null) { try { await addDoc(collection(db, 'user'), { pfp: url, userName, uid, timestamp: serverTimestamp(), time: Date(), }) if (!photoEdit) { navigate('/test') } console.log('data send') } catch (err) { console.log(err) } } } const [displayName, setDisplayName] = useState(null) const [displayPhoto, setDisplayPhoto] = useState(null) const [userProfiles, setUserProfiles] = useState(null) useEffect(() => { const q = query(collection(db, 'user'), orderBy('timestamp')) const unsub = onSnapshot(q, (querySnapShot) => { let photo = [] querySnapShot.forEach((doc) => { photo.push({ ...doc.data(), id: doc.id }) }) console.log(photo) console.log('data resived') let userUid = photo .filter((item) => user.uid === item.uid) .map((item) => item.pfp) setDisplayPhoto( userUid.filter((val, index) => userUid.length - 1 <= index) ) let userUidName = photo .filter((item) => user.uid === item.uid) .map((item) => item.userName) let photoFilter = userUidName.filter((val, index) => userUidName.length - 1 <= index) setDisplayName(photoFilter) setUserProfiles(photo) console.log(displayPhoto) }) console.log('re render ? ') return () => unsub() }, [user])
数据库结构

解决方案
1. 调整Firestore数据存储策略
不再使用addDoc创建随机ID的文档,而是以用户的uid作为文档ID,将用户资料直接存储在user/{uid}路径下。这样每次更新时直接覆盖该文档,不会产生重复记录。
2. 修改头像提交逻辑
使用setDoc替代addDoc,并设置merge: true参数,实现“存在则更新,不存在则创建”的效果:
const handlePfpSubmit = async () => { const { uid } = user if (url !== null) { try { // 以用户uid作为文档ID,直接覆盖/创建用户资料文档 await setDoc(doc(db, 'user', uid), { pfp: url, userName, uid, timestamp: serverTimestamp(), time: Date(), }, { merge: true }) // merge参数保留原有未修改字段 if (!photoEdit) { navigate('/test') } console.log('data updated') } catch (err) { console.log(err) } } }
3. 优化数据拉取逻辑
无需监听整个user集合并过滤数据,直接监听当前用户的文档,获取最新的头像和用户名:
useEffect(() => { if (!user?.uid) return // 直接监听当前用户的文档 const userDocRef = doc(db, 'user', user.uid) const unsub = onSnapshot(userDocRef, (docSnap) => { if (docSnap.exists()) { const userData = docSnap.data() setDisplayPhoto(userData.pfp) setDisplayName(userData.userName) console.log('user data updated') } else { console.log('no user data found') } }) return () => unsub() }, [user])
4. Firebase Storage自动覆盖旧图片
当前上传Storage时使用的路径是image${user.uid},Firebase Storage默认会覆盖同名文件,因此旧的头像文件会被新文件自动替换,无需额外删除操作。
内容的提问来源于stack exchange,提问作者Giorgi Kutateladze
相关产品推荐
相关产品推荐

