You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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 文档结构

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 14:56:30