如何在ReactJS中删除Firestore文档双数组对应的行数据
React + Firestore 实现指定行删除功能(同步删除两个数组对应元素)
问题描述
在ReactJS项目中使用Firestore数据库,用户文档包含value(对应Food)和quantity两个数组,前端将两个数组对应位置的元素组合为表格行展示。需要实现删除指定表格行的功能,即同时删除Firestore文档中两个数组内对应位置的元素。当前删除函数会直接删除整个字段,无法满足需求。
现有代码
Firebase 删除函数(当前存在问题)
const handleDelete = async () => { onAuthStateChanged(auth, async (user) => { if (user) { const uid = user.uid; const snapshot = await getDoc(doc(db, 'data', uid)); if (snapshot.exists()) { await snapshot.update({ value: FieldValue.delete(), number: FieldValue.delete(), }) } else { console.log('User doc missing'); } } else { console.log('User not logged in'); } }) }
表格渲染代码
<table className='table table-hover'> <thead> <tr> <th>#</th> <th>Food</th> <th>Quantity (gr)</th> {/* <th>Calories (kcl)</th> */} </tr> </thead> <tbody> {data.map((row, index) => { // calculate calories let calories = Math.ceil(row.quantity * 1.35); return ( <> <tr key={index}> <td>{index + 1}</td> <td className='font'>{row.value}</td> <td className='font'>{row.quantity}</td> {/* <td className='font'>{calories}</td> */} <p onClick={handleDelete}>X</p> </tr> </> ) })} </tbody> </table>
数据获取代码
NutritionDataInstance作为单例,用于跨文件共享数组,避免重复值覆盖,替代Firebase的arrayUnion函数
useEffect(() => { onAuthStateChanged(auth, async (user) => { let foodNames = []; let quantities = []; let foods = []; if (user) { const uid = user.uid; const snapshot = await getDoc(doc(db, 'data', uid)); if (snapshot.exists()) { const { value, quantity } = snapshot.data(); let s = 0; for (let i = 0; i < value.length; i++) { foods.push({ value: value[i], quantity: quantity[i] }); foodNames.push(value[i]); quantities.push(quantity[i]); // sum of all calories setSum(s = s + Math.ceil(quantity[i] * 1.35)) } NutritionDataInstance.foodNames = foodNames; NutritionDataInstance.quantities = quantities; setData(foods); } else { console.log('User doc missing'); } } else { console.log('User not logged in'); setData([]); } }); }, [data]);
Firebase 文档结构

解决方案
Firestore没有直接删除数组指定索引元素的API,需要先获取当前数组、移除目标索引元素后再更新文档。以下是修改步骤:
1. 优化用户状态管理
在组件中新增状态存储用户UID,避免重复调用onAuthStateChanged:
import { useState, useEffect } from 'react'; import { auth, db } from './firebaseConfig'; // 按需导入你的Firebase配置 import { doc, getDoc, updateDoc } from 'firebase/firestore'; const YourComponent = () => { const [uid, setUid] = useState(null); const [data, setData] = useState([]); const [sum, setSum] = useState(0); // ... 其他代码 }
2. 修改数据获取逻辑(避免无限循环)
移除data作为useEffect依赖,改用用户状态监听,并存储UID:
useEffect(() => { const unsubscribe = onAuthStateChanged(auth, async (user) => { if (user) { const currentUid = user.uid; setUid(currentUid); const docRef = doc(db, 'data', currentUid); const snapshot = await getDoc(docRef); if (snapshot.exists()) { const { value, quantity } = snapshot.data(); const foods = []; let totalSum = 0; for (let i = 0; i < value.length; i++) { foods.push({ value: value[i], quantity: quantity[i] }); totalSum += Math.ceil(quantity[i] * 1.35); } NutritionDataInstance.foodNames = value; NutritionDataInstance.quantities = quantity; setData(foods); setSum(totalSum); } else { console.log('用户文档不存在'); } } else { console.log('用户未登录'); setData([]); setUid(null); } }); // 组件卸载时取消监听 return () => unsubscribe(); }, []);
3. 实现指定索引删除函数
修改handleDelete,接收目标行索引,处理数组更新:
const handleDelete = async (index) => { if (!uid) { console.log('用户未登录'); return; } const docRef = doc(db, 'data', uid); const snapshot = await getDoc(docRef); if (snapshot.exists()) { const { value, quantity } = snapshot.data(); // 生成移除目标索引后的新数组 const newValue = value.filter((_, i) => i !== index); const newQuantity = quantity.filter((_, i) => i !== index); // 更新Firestore文档 await updateDoc(docRef, { value: newValue, quantity: newQuantity }); // 更新本地状态,提升用户体验 const newData = data.filter((_, i) => i !== index); setData(newData); // 更新总热量 const newSum = newData.reduce((acc, row) => acc + Math.ceil(row.quantity * 1.35), 0); setSum(newSum); // 更新单例数据 NutritionDataInstance.foodNames = newValue; NutritionDataInstance.quantities = newQuantity; } else { console.log('用户文档不存在'); } }
4. 绑定删除按钮与索引
修改表格中的删除按钮,传递当前行的索引:
<p onClick={() => handleDelete(index)}>X</p>
关键说明
- 原代码使用
FieldValue.delete()会直接删除整个字段,而非数组内的元素,这是核心问题。 - 通过
filter方法移除指定索引的元素,再更新Firestore文档,实现精确删除。 - 更新本地状态可以避免等待Firestore同步,提升界面响应速度。
内容的提问来源于stack exchange,提问作者Ashka
相关产品推荐
相关产品推荐

