AngularFire2更新列表后内存泄漏:点击按钮数值无限自增
解决Firebase列表值无限递增的问题
看起来你碰到了实时数据库的无限循环陷阱——每次点击按钮触发递增后,snapshotChanges()的实时监听会捕捉到数据更新,然后再次执行递增逻辑,就这么一直循环下去,直到你关闭标签页才会停止。
问题根源
你代码里用的snapshotChanges()是持续订阅数据变化的Observable,只要数据库里的数据有更新,它就会再次触发回调函数。当你在回调里修改数据时,更新后的变化又会触发回调,形成了无限循环。
修复方案
我们需要让订阅只执行一次,避免持续监听数据变化,同时可以结合Firebase的原子递增操作来优化逻辑:
方案1:使用take(1)限制订阅次数
通过take(1)操作符让Observable只获取一次数据就取消订阅,这样更新数据后不会再次触发回调:
import { take } from 'rxjs/operators'; onRecipeUpvote(recipe: Recipe) { // 匹配目标recipe的查询 const recipeList = this.ngFireDB.list<Recipe>('/recipes', ref => ref.orderByChild('name').equalTo(recipe.name) ); // 只订阅一次数据,获取后立即取消订阅 recipeList.snapshotChanges().pipe(take(1)).subscribe(actions => { actions.forEach(action => { const key = action.payload.key; const currentRecipe = action.payload.val() as Recipe; // 递增点赞数(如果upvotes未定义则默认从0开始) const newUpvoteCount = (currentRecipe.upvotes || 0) + 1; // 更新对应节点的点赞值 this.ngFireDB.object(`/recipes/${key}`).update({ upvotes: newUpvoteCount }); }); }); }
方案2:使用Firebase原子递增操作(更推荐)
直接调用Firebase的原子递增API,不需要先读取本地数据再写入,既避免并发问题,也能简化逻辑:
import { take } from 'rxjs/operators'; onRecipeUpvote(recipe: Recipe) { const recipeList = this.ngFireDB.list<Recipe>('/recipes', ref => ref.orderByChild('name').equalTo(recipe.name) ); recipeList.snapshotChanges().pipe(take(1)).subscribe(actions => { actions.forEach(action => { const key = action.payload.key; // 使用实时数据库的原子递增,直接在服务器端完成数值增加 this.ngFireDB.object(`/recipes/${key}`).update({ upvotes: firebase.database.ServerValue.increment(1) }); }); }); }
关键说明
take(1)是核心:它确保Observable只执行一次回调就自动取消订阅,从根源上避免了无限循环。- 原子递增优势:
ServerValue.increment(1)是在Firebase服务器端执行的操作,不会出现多用户同时点击时的数值覆盖问题,比本地读取再写入更安全高效。
内容的提问来源于stack exchange,提问作者Muizz Mahdy
相关产品推荐
相关产品推荐

