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

Firebase批量创建/更新文档及集合切换技术求助

问题描述
  • 需求:以Firebase默认集合数据为初始值,允许用户修改后批量保存到个人专属集合;用户保存自定义值后,后续加载时优先读取个人集合数据作为输入框占位符,不再读取默认集合。
  • 当前问题:通过data.map渲染了带输入框的表格,但点击保存时只能创建单个文档,无法批量提交所有输入框的修改值。
解决方案

1. 换个方式存修改值

别用单个变量存单个奶酪的名字和成本了,用一个对象把所有奶酪的修改后成本都装起来(比如{ "芝士A": "10", "芝士B": "15" }),这样能区分每个奶酪的修改值,避免单个状态搞混不同数据。

2. 先读自己的数据,没有再读默认的

先监听个人集合,如果里面有数据,就用这个数据当输入框的占位符;要是没有,再去读默认集合的初始值,这样保存后下次打开就直接用自己的数据了。

3. 批量提交所有修改

用Firebase的writeBatch一次性把所有修改都提交,不用一个个创建文档,这样就能批量保存所有输入框的值,还能保证操作的原子性(要么全成功,要么全失败)。


修改后的完整代码
import { useState, useEffect } from 'react';
import { collection, query, orderBy, onSnapshot, writeBatch, doc, serverTimestamp } from 'firebase/firestore';
import { useAuthContext } from './替换为你的AuthContext路径';
import { db } from './替换为你的Firebase配置路径';

export default function CheeseCostForm() {
  const { user } = useAuthContext();
  // 个人专属集合引用
  const personalColRef = collection(db, `users/${user.uid}/cheese`);
  // 默认集合引用
  const defaultColRef = collection(db, '/default/cheeseCosts/cheeseCosts');
  const defaultQuery = query(defaultColRef, orderBy('listOrder', 'asc'));

  // 存储所有奶酪的修改后成本:键为奶酪名称,值为修改后的成本
  const [cheeseCosts, setCheeseCosts] = useState({});
  // 最终展示的数据源(优先个人集合,无数据则用默认集合)
  const [displayData, setDisplayData] = useState([]);

  // 监听数据源:先查个人集合,无数据再查默认集合
  useEffect(() => {
    const unsubscribePersonal = onSnapshot(personalColRef, (snapshot) => {
      const personalData = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
      if (personalData.length > 0) {
        setDisplayData(personalData);
        // 初始化修改状态为个人数据的成本
        const initialCosts = {};
        personalData.forEach(item => {
          initialCosts[item.cheeseName] = item.cheeseCost;
        });
        setCheeseCosts(initialCosts);
        return; // 有个人数据就不再监听默认集合
      }

      // 无个人数据时监听默认集合
      const unsubscribeDefault = onSnapshot(defaultQuery, (snapshot) => {
        const defaultData = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
        setDisplayData(defaultData);
        // 初始化修改状态为默认数据的成本
        const initialCosts = {};
        defaultData.forEach(item => {
          initialCosts[item.cheeseName] = item.cheeseCost;
        });
        setCheeseCosts(initialCosts);
      });

      return unsubscribeDefault;
    });

    return unsubscribePersonal;
  }, [user.uid, personalColRef, defaultQuery]);

  // 单个输入框的修改事件处理
  const handleCostChange = (cheeseName, value) => {
    setCheeseCosts(prev => ({
      ...prev,
      [cheeseName]: value
    }));
  };

  // 批量保存所有修改
  const updateCheese = async (e) => {
    e.preventDefault();
    const batch = writeBatch(db);

    // 遍历所有展示的数据,批量写入个人集合
    displayData.forEach(item => {
      // 若未修改则保留原值
      const finalCost = cheeseCosts[item.cheeseName] || item.cheeseCost;
      // 用奶酪名称作为文档ID,确保每个奶酪唯一对应一个文档
      const docRef = doc(personalColRef, item.cheeseName);
      batch.set(docRef, {
        cheeseName: item.cheeseName,
        cheeseCost: finalCost,
        uid: user.uid,
        dateLastChanged: serverTimestamp()
      }, { merge: true }); // 合并写入,避免覆盖已存在的字段
    });

    await batch.commit();
    alert('所有奶酪成本已更新');
  };

  return (
    <form onSubmit={updateCheese}>
      <table>
        <thead>
          <tr>
            <th>奶酪名称</th>
            <th>当前成本</th>
            <th>修改成本</th>
          </tr>
        </thead>
        <tbody>
          {displayData.map(costs => (
            <tr key={costs.id || costs.cheeseName}>
              <td>{costs.cheeseName}</td>
              <td>{costs.cheeseCost}</td>
              <td>
                <input
                  type="text"
                  maxLength="3"
                  value={cheeseCosts[costs.cheeseName] || costs.cheeseCost}
                  onChange={(e) => handleCostChange(costs.cheeseName, e.target.value)}
                  placeholder={costs.cheeseCost}
                />
              </td>
            </tr>
          ))}
        </tbody>
      </table>
      <button className="btn" type="submit">更新奶酪成本</button>
    </form>
  );
}

内容的提问来源于stack exchange,提问作者Apersu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 01:41:04