You need to enable JavaScript to run this app. Firebase客户端在JS时钟同步中如何删除非自身创建的文档
优惠活动
大模型
产品
解决方案
定价
更多

Firebase客户端在JS时钟同步中如何删除非自身创建的文档

问题描述

我正通过每秒向Firestore写入数据的方式同步两个JavaScript客户端的时钟,随后让第二个远程“从”客户端订阅该文档。

目前该方案运行正常,我可通过如下创建文档引用的方式实时读取文档变更:

const useCloudTimer = (isMaster, opponentCCID, userCCID) => {
    const dispatch = useDispatch();
    const { localRemainingTimeMS, clockTimeOut } = useSelector((state) => state.clock);

    const timerRef = useRef(null);

    useEffect(() => {
        const db = firebase.firestore();
        timerRef.current = db.collection('timers').doc(`${isMaster
            ? userCCID + opponentCCID
            : opponentCCID + userCCID
            }`);

        dispatch(clock(CLOCK, { cloudTimerDbRef: timerRef.current }));
    }, []);

    const pushTimer = async (duration) => {
        try {
            await timerRef.current.set({ duration });
        } catch (error) {
            logger.error(error, 'cloud timer');
        }
    };

    useEffect(() => { if (isMaster) pushTimer(localRemainingTimeMS); }, [localRemainingTimeMS]);

    const getTimer = async () => {
        try {
            const unsubscribeRemoteTimer = await timerRef.current.onSnapshot((doc) => {
                if (!clockTimeOut && doc.exists) {
                    const duration = Number(doc.data().duration);
                    dispatch(clock(CLOCK, { remoteRemainingTimeMS: duration }));
                }
            });
            if (clockTimeOut) unsubscribeRemoteTimer().then((arg) => console.log('unsubscribeRemoteTimer', arg));
        } catch (error) {
            logger.error(error, 'getTimer');
        }
    };

    useEffect(() => { if (!isMaster) getTimer(); }, []);
};

export default useCloudTimer;

目前存在的问题是需要删除该文档时,若由非文档创建者发起删除操作,会出现同名文档先被短暂创建、随后再被删除的情况。该现象发生瞬间可观察到出现了两个ID反向的同名文档:

  • 文档处于写入状态时显示为绿色,删除状态时显示为红色。

我的文档引用会存储在redux中,需要时通过状态存储调用,删除逻辑代码如下:

export const deleteCloudTimer = async (timerRef) => {
    if (timerRef) {
        try {
            await timerRef.delete();
        } catch (error) {
            logger.error(error, 'Error removing document: ');
        }
    }
};

我的Firebase客户端应用要如何才能删除非自身创建的文档?


解决方案

1. 先修复文档ID拼接逻辑

字符串拼接不满足交换律,你当前的ID生成逻辑isMaster ? userCCID + opponentCCID : opponentCCID + userCCID会因为两个端的主从身份不同,生成两个完全不同的文档ID,这就是你看到两个“同名文档”的核心原因。
替换为排序后拼接的逻辑,保证两个端生成的文档ID完全一致:

// 生成双方通用的统一文档ID
const getTimerDocId = (userCCID, opponentCCID) => {
  return [userCCID, opponentCCID].sort().join('_');
}
// 替换原来的doc入参
timerRef.current = db.collection('timers').doc(getTimerDocId(userCCID, opponentCCID));

2. 配置Firestore安全规则放开删除权限

Firestore默认的安全规则仅允许文档创建者执行删除操作,非创建者的删除请求会被直接拦截,需要修改timers集合的规则,允许两个关联用户对文档执行全部操作:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /timers/{docId} {
      // 校验当前登录用户属于该文档对应的两个用户之一,即拥有读写、删除权限
      allow read, write, delete: if request.auth != null && 
        (docId.split('_')[0] == request.auth.uid || docId.split('_')[1] == request.auth.uid);
    }
  }
}

3. 补充防重复写入逻辑

主端当前是无判断直接每秒set写入,若从端触发删除后主端还未收到停止指令,会再次把已删除的文档写入恢复。修改pushTimer逻辑,写入前先校验文档是否存在,避免已删除的文档被重新创建:

const pushTimer = async (duration) => {
  try {
    const docSnap = await timerRef.current.get();
    // 仅当文档存在时才继续写入
    if (docSnap.exists) {
      await timerRef.current.set({ duration });
    }
  } catch (error) {
    logger.error(error, 'cloud timer');
  }
};

内容的提问来源于stack exchange,提问作者Mr. Robot

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.24 10:36:03