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

Angular10项目Firebase snapshotChanges()重复执行问题求助

解决Angular 10中Firebase snapshotChanges重复执行问题

问题核心原因

  1. 重复订阅:如果组件多次初始化(比如路由复用、未正确清理订阅),会导致snapshotChanges()被多次订阅,每次集合变更都会触发多轮执行。
  2. Firebase本地/服务器双触发:Firebase实时监听会触发两次回调——本地写入成功时触发一次(未同步到服务器),服务器确认变更后再触发一次,这就是你执行save_data()时监听逻辑触发两次的原因。
  3. get()方案使用错误:get()返回Observable,仅通过pipe()不会触发执行,必须配合subscribe()或转换为Promise后使用await。

解决方案

1. 修复snapshotChanges重复执行(保留监听能力)

通过RxJS操作符管理订阅生命周期,并过滤本地未确认的变更:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { AngularFirestore } from '@angular/fire/firestore';

@Component({
  // 组件元数据
})
export class YourComponent implements OnInit, OnDestroy {
  private destroy$ = new Subject<void>();
  current_layout_data: any[] = [];
  current_store_uid: string; // 已定义的当前店铺ID
  current_date = new Date();

  constructor(private afs: AngularFirestore) {}

  ngOnInit(): void {
    this.afs.collection('/layouts', ref => 
      ref.where('store_uid', '==', this.current_store_uid)
    )
    .snapshotChanges()
    .pipe(
      // 只处理服务器确认的变更,过滤本地未同步的触发
      filter(actions => actions.every(action => !action.payload.doc.metadata.hasPendingWrites)),
      // 组件销毁时自动取消订阅,避免内存泄漏和重复订阅
      takeUntil(this.destroy$)
    )
    .subscribe(data => {
      console.log("layout 111");
      // 简化数据处理逻辑
      this.current_layout_data = data
        .map(docAction => docAction.payload.doc.data())
        .filter(item => {
          const startDate = item.start_date.toDate();
          const endDate = item.end_date.toDate();
          return startDate <= this.current_date && endDate > this.current_date;
        });
      this.layoutData();
    });

    // 同时处理layout_form.valueChanges的订阅,避免重复触发
    this.layout_form.valueChanges
      .pipe(takeUntil(this.destroy$))
      .subscribe(formData => {
        // 表单逻辑处理
      });
  }

  ngOnDestroy(): void {
    // 触发所有订阅取消
    this.destroy$.next();
    this.destroy$.complete();
  }

  async save_data(data) {
    await this.afs.doc(`/layouts/${this.current_template_id}`).update({
      layout_json_saved: data.layout_json_temp
    });
  }

  layoutData(): void {
    // 你的layoutData逻辑
  }
}

2. 修复get()单次获取方案

如果需要单次获取数据而非持续监听,正确写法如下:

// 方式1:使用subscribe
console.log("Flow coming here");
this.afs.collection('/layouts', ref => 
  ref.where('store_uid', '==', this.current_store_uid)
)
.get()
.pipe(
  map(querySnapshot => {
    console.log("Data received:-", querySnapshot);
    this.current_layout_data = querySnapshot.docs
      .map(doc => doc.data())
      .filter(item => {
        const startDate = item.start_date.toDate();
        const endDate = item.end_date.toDate();
        return startDate <= this.current_date && endDate > this.current_date;
      });
    this.layoutData();
    return this.current_layout_data;
  })
)
.subscribe();

// 方式2:使用await + toPromise()
async getLayoutData() {
  console.log("Flow coming here");
  const querySnapshot = await this.afs.collection('/layouts', ref => 
    ref.where('store_uid', '==', this.current_store_uid)
  ).get().toPromise();
  
  console.log("Data received:-", querySnapshot);
  this.current_layout_data = querySnapshot.docs
    .map(doc => doc.data())
    .filter(item => {
      const startDate = item.start_date.toDate();
      const endDate = item.end_date.toDate();
      return startDate <= this.current_date && endDate > this.current_date;
    });
  this.layoutData();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 21:43:29