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

如何用React+Material UI ProgressBar实现关联Firestore的预约倒计时功能

第一步:修正Firestore文档写入逻辑

你现有的expiryDate计算逻辑存在错误,既没有声明event变量,setHours(72)也不是「当前时间加72小时」的正确写法,同时推荐使用Firebase服务器时间避免本地时间误差,修正后的写入代码如下:

import { serverTimestamp } from 'firebase/firestore';

// 写入文档逻辑
reserveDB.doc(item.productID).set({
  productID: item.productID,
  image: item.image,
  price: item.price,
  productName: item.productName,
  isReserved: true,
  startDate: serverTimestamp(), // 用服务器时间戳避免本地时间误差
  expiryDate: new Date(Date.now() + 72 * 60 * 60 * 1000), // 72小时后的时间戳
});

如果需要完全避免本地时间篡改,可以配合Firebase云函数在文档创建时自动计算expiryDate。

第二步:实现绑定Firestore的Material UI进度条倒计时

完整实现逻辑如下,包含实时监听Firestore数据、进度计算、到期自动重置状态的功能:

import { useState, useEffect } from 'react';
import { LinearProgress } from '@mui/material';
import { doc, onSnapshot, updateDoc } from 'firebase/firestore';
import { reserveDB } from './你的firebase配置文件路径';

const ReserveProgress = ({ productID }) => {
  const [progress, setProgress] = useState(0);
  const [isReserved, setIsReserved] = useState(false);
  const TOTAL_RESERVE_TIME = 72 * 60 * 60 * 1000; // 总预留时长72小时,单位毫秒

  useEffect(() => {
    // 监听对应商品的Firestore文档实时变化
    const unsub = onSnapshot(doc(reserveDB, productID), (docSnapshot) => {
      if (!docSnapshot.exists()) return;
      const data = docSnapshot.data();
      setIsReserved(data.isReserved);
      
      // 如果商品处于预留状态,启动倒计时
      if (data.isReserved && data.expiryDate) {
        // 兼容Firestore Timestamp类型和毫秒数字格式
        const expiryTime = data.expiryDate.toMillis ? data.expiryDate.toMillis() : data.expiryDate;
        
        // 每秒更新一次进度
        const timer = setInterval(() => {
          const now = Date.now();
          const remainingTime = expiryTime - now;
          
          if (remainingTime <= 0) {
            // 到期自动重置预留状态
            updateDoc(doc(reserveDB, productID), { isReserved: false });
            setProgress(0);
            clearInterval(timer);
            return;
          }
          
          // 计算进度值,限制在0-100区间
          const calculatedProgress = Math.max(0, Math.min(100, (remainingTime / TOTAL_RESERVE_TIME) * 100));
          setProgress(calculatedProgress);
        }, 1000);

        // 状态变化时清理旧定时器
        return () => clearInterval(timer);
      } else {
        setProgress(0);
      }
    });

    // 组件卸载时取消Firestore监听
    return () => unsub();
  }, [productID]);

  return isReserved ? (
    <LinearProgress variant="determinate" value={progress} />
  ) : null;
};

export default ReserveProgress;
补充说明

如果需要更高的安全性,建议新增Firebase云函数定时扫描所有isReserved=true的文档,把expiryDate小于当前时间的文档批量重置isReserved为false,避免前端定时器失效或者用户篡改本地时间导致的状态异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 08:18:00