Angular中数据库变更后ngOnInit多次调用的问题排查
Angular Firestore 删除记录后数据重复加载问题
我开发的Angular应用中,用户点击按钮会从Firestore数据库删除记录。控制台显示ngOnInit先以旧数据调用1次,再以更新后的新数据调用2次,导致数组中存入3条数据而非1条。尝试用take(1)操作符仅获取首次响应,但得到的是变更前的旧数据;尝试启用OnPush变更检测却出现空白页面。
相关代码
swap-requests.html
<ion-content> <ion-grid class="ion-grid-width-md" *ngIf="user" > <tr *ngFor="let swaps of userSwapRequests;"> <ion-row class="ion-padding" size="auto"> <ion-col> <td *ngFor="let user of swaps.userCard"> <ion-img [src]="user.images.small" style="background: #F2F2F2;"></ion-img> </td> </ion-col> <ion-col> <td *ngFor="let friend of swaps.friendCard"> <ion-img [src]="friend.images.small" style="background: #F2F2F2;"></ion-img> </td> </ion-col> </ion-row> <ion-row> <ion-col> <ion-button expand="block" color="danger" (click)="cancelSwap(swaps.swapData, swaps)">Cancel Swap</ion-button> </ion-col> </ion-row> </tr> </ion-grid> </ion-content>
swap-requests.page.ts
@Component({ selector: 'app-swap-requests', templateUrl: './swap-requests.page.html', styleUrls: ['./swap-requests.page.scss'], }) export class SwapRequestsPage implements OnInit { user: any[] = []; userSwapRequests: any[] = []; currentFriend: User[] = []; swapArray: any[] = []; constructor( private dataService: DataService, private pokeService: PokemoncardsService, private ToastCtrl: ToastController, ) { } ngOnInit() { this.dataService.loadUser(environment.currentUser).pipe(take(1)).subscribe((res: User[]) => { this.user.push(res); console.log('user', this.user); this.getUserPendingRequests(); }); } // 获取好友发起的请求 getUserPendingRequests() { this.userSwapRequests = []; let userSwapsList = this.user.map(data => data.swap_request); this.buildSwapArray(this.userSwapRequests, userSwapsList); } getFriend(value: any) { let friend: any[] = []; this.dataService.loadUser(value).subscribe((res: User[]) => { friend.push(res); }); return friend; } getPokemonCard(number: any) { let result: any = []; this.pokeService.getPokeCard(number).subscribe(res => { result.push(res); }); return result; } // 整合API响应和好友数据,生成新数组 async buildSwapArray(swaplist: any, userList: any) { userList.forEach((array: any) => { array.forEach((swap: any) => { let card: any; let usercard: any; let friend: any; Object.entries(swap).find(([key, value]) => { if (key === 'friend_card') { card = this.getPokemonCard(value); } if (key === 'user_card') { usercard = this.getPokemonCard(value);; } if (key === 'friend_id') { friend = this.getFriend(value); } }) let newArray = { 'friendCard': card, 'userCard': usercard, 'friend': friend, 'swapData': swap, } swaplist.push(newArray); console.log('swapArray', this.userSwapRequests); }) }) } async cancelSwap(swapArray: any) { console.log('swapArray', swapArray); this.dataService.deleteSwapRequest(swapArray); let id = this.user.map(data => data.id); let friend: any; id.forEach((array) => { friend = array; }) let friendArray: any = { 'friend_card': swapArray.user_card, 'friend_id': friend, 'user_card': swapArray.friend_card, } let friendId = swapArray.friend_id; this.dataService.deleteFriendSwapRequest(friendArray, friendId); console.log('testsplice', this.userSwapRequests); const toast = await this.ToastCtrl.create({ message: 'Swap confirmed', duration: 2000 }); toast.present(); } }
DataService
export class User { id?: string; cards: [] = []; user_name: string | undefined; friends: [] = []; wishlist: [] = []; swaps: [] = []; pending_swap: [] = []; avatar: string | undefined; swap_request: [] = []; confirmed_swap: [] = []; } @Injectable({ providedIn: 'root' }) export class DataService { constructor(private firestore: Firestore) { } loadUser(id: any): Observable<User[]> { const result = doc(this.firestore, `users/${id}`); return docData(result, { idField: 'id' }) as Observable<User[]>; } deleteSwapRequest(swapRequest: any) { const cardsIdRef = doc(this.firestore, `users/${environment.currentUser}`); return updateDoc(cardsIdRef, { swap_request: arrayRemove(swapRequest) }); } deleteFriendSwapRequest(swapRequest: any, friend:any) { const cardsIdRef = doc(this.firestore, `users/${friend}`); return updateDoc(cardsIdRef, { pending_swap: arrayRemove(swapRequest) }); } }
问题分析与修复方案
核心问题
- Firestore实时监听数据堆积:
docData默认是实时监听文档变化的Observable,每次文档更新(包括删除操作后)都会推送新数据。原代码用push添加数据,导致旧数据残留;用take(1)则会丢失后续更新。 - 异步数据处理逻辑错误:
getFriend和getPokemonCard同步返回空数组,异步订阅的结果无法正确关联到视图,导致数据加载异常。 - OnPush变更检测失效:启用OnPush后,组件依赖的数据未通过可观察对象或输入属性触发变更检测,导致视图空白。
具体修复步骤
1. 正确处理Firestore实时数据
去掉take(1),保持实时监听,同时直接覆盖user数组避免数据堆积:
ngOnInit() { this.dataService.loadUser(environment.currentUser).subscribe((res: User[]) => { // 直接覆盖数组,替换旧数据 this.user = [res]; console.log('user', this.user); // 每次数据更新时重新构建请求列表 this.getUserPendingRequests(); }); }
2. 修复异步数据获取逻辑
将getFriend和getPokemonCard改为返回Observable,用forkJoin并行处理异步请求:
// 修改两个方法返回Observable getFriend(value: any): Observable<User[]> { return this.dataService.loadUser(value); } getPokemonCard(number: any): Observable<any> { return this.pokeService.getPokeCard(number); } // 重写buildSwapArray确保数据就绪后再构建数组 async buildSwapArray(swaplist: any, userList: any) { swaplist.length = 0; for (const array of userList) { for (const swap of array) { // 并行获取所有异步数据 const [friendCard, userCard, friend] = await forkJoin([ this.getPokemonCard(swap.friend_card), this.getPokemonCard(swap.user_card), this.getFriend(swap.friend_id) ]).toPromise(); swaplist.push({ friendCard: [friendCard], userCard: [userCard], friend: [friend], swapData: swap, }); } } }
3. 删除后手动同步本地数据(提升体验)
在删除操作完成后,手动从userSwapRequests移除对应项:
async cancelSwap(swapData: any, swapItem: any) { // 等待两个删除操作完成 await Promise.all([ this.dataService.deleteSwapRequest(swapData), this.dataService.deleteFriendSwapRequest({ friend_card: swapData.user_card, friend_id: this.user[0].id, user_card: swapData.friend_card, }, swapData.friend_id) ]); // 移除本地列表中的项 const index = this.userSwapRequests.indexOf(swapItem); if (index > -1) { this.userSwapRequests.splice(index, 1); } const toast = await this.ToastCtrl.create({ message: 'Swap cancelled', duration: 2000 }); toast.present(); }
4. 解决OnPush变更检测问题(可选)
若要启用OnPush,需用BehaviorSubject管理数据并配合async管道:
import { ChangeDetectionStrategy, BehaviorSubject } from '@angular/core'; @Component({ selector: 'app-swap-requests', templateUrl: './swap-requests.page.html', styleUrls: ['./swap-requests.page.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class SwapRequestsPage implements OnInit { private userSubject = new BehaviorSubject<User[]>([]); user$ = this.userSubject.asObservable(); private swapRequestsSubject = new BehaviorSubject<any[]>([]); userSwapRequests$ = this.swapRequestsSubject.asObservable(); ngOnInit() { this.dataService.loadUser(environment.currentUser).subscribe((res: User[]) => { this.userSubject.next([res]); this.getUserPendingRequests(res); }); } async getUserPendingRequests(user: User) { const swaplist: any[] = []; await this.buildSwapArray(swaplist, [user.swap_request]); this.swapRequestsSubject.next(swaplist); } }
模板中使用async管道:
<ion-grid class="ion-grid-width-md" *ngIf="user$ | async as user" > <tr *ngFor="let swaps of userSwapRequests$ | async;"> <!-- 其余内容不变 --> </tr> </ion-grid>
内容的提问来源于stack exchange,提问作者AimlesslyTrying
相关产品推荐
相关产品推荐

