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

触发ngRx的SetSensors Action后应用崩溃,无报错如何排查?

问题描述

我编写了如下ngRx reducer:

export type Action = SensorsActions.SensorsActions

const defaultState: ISensor[] = []

export function sensorsReducer(state: ISensor[] = defaultState, action: any){
  switch (action.type){
    case SensorsActions.ADD_SENSOR:
      return [...state, action.payload]
    case SensorsActions.DELETE_SENSOR:
      return state.filter(s => s.id !== action.payload.id)
    case SensorsActions.UPDATE_SENSOR:
      let number = state.findIndex(s => s.id == action.payload.id)
      const sensors = [...state]
      sensors[number] = action.payload
      return sensors
    case SensorsActions.SET_SENSORS:
      return action.payload
    default:
      return state
  }
}

组件中定义了Observable状态:

sensors$: Observable<ISensor[]>
constructor(
  private sensorsService: SensorsService,
  private store: Store<SensorsTableState>
) {
  this.sensors$ = this.store.select('sensors')
}

UI中展示传感器对象的表格,并有一个用于过滤对象的按钮:

<button class="search-button" (click)="filterSensors()">
filterSensors(): void {
  if (this.searchQuery !== undefined){
    this.sensors$.subscribe(sensors => {
      const result = filterSensors(sensors, this.searchQuery)
      this.store.dispatch(new SensorsActions.SetSensors(result))
    })
    this.setPages()
  }
}

export const filterSensors = (sensors: ISensor[], query: string): ISensor[] => {
  return sensors.filter(sensor => {
    return Object.values(sensor).find(value => {
      const isValidValue = value && (typeof value === "string" || typeof value === "number")
      return isValidValue && value.toString().toLowerCase().includes(query.toLowerCase())
    })
  })
}

点击该按钮后应用崩溃,甚至无法刷新页面,但控制台与终端均无错误提示。过滤方法可正确返回所需对象,且在ngOnInit方法中通过相同的dispatch操作设置从服务器获取的传感器时运行正常,请问问题出在哪里?


问题原因与解决方案

核心问题:无限循环导致内存耗尽

你在filterSensors方法里的逻辑触发了无限循环:

  • 订阅store.select('sensors')返回的sensors$后,在回调中dispatchSET_SENSORS更新store的sensors状态
  • store状态更新会让sensors$再次发射新值,触发订阅回调重新执行,再次dispatchSET_SENSORS
  • 循环往复短时间内产生大量状态更新,直接耗尽浏览器内存,导致应用崩溃,控制台来不及打印错误。

ngOnInit中的dispatch没问题,是因为它只执行一次,不会触发后续的订阅回调循环。

解决方案

方案1:用RxJS管道处理过滤(推荐)

过滤属于视图层展示逻辑,无需修改store原始数据。通过combineLatest组合原始数据和查询条件,自动更新过滤结果:

import { combineLatest, BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';

// 组件内新增
filteredSensors$: Observable<ISensor[]>;
private searchQuerySubject = new BehaviorSubject<string>('');

constructor(private store: Store<SensorsTableState>) {
  this.sensors$ = this.store.select('sensors');
  // 组合数据流,自动计算过滤结果
  this.filteredSensors$ = combineLatest([this.sensors$, this.searchQuerySubject]).pipe(
    map(([sensors, query]) => query ? filterSensors(sensors, query) : sensors)
  );
}

filterSensors(): void {
  // 仅更新查询条件,无需订阅和dispatch
  this.searchQuerySubject.next(this.searchQuery);
  this.setPages();
}

之后UI直接绑定filteredSensors$即可,完全不改动store原始数据。

方案2:封装NgRx Selector(适合多组件复用)

把过滤逻辑封装成NgRx selector,由selector负责计算过滤结果:

// selector文件中
import { createSelector } from '@ngrx/store';

export const selectSensors = (state: SensorsTableState) => state.sensors;

export const selectFilteredSensors = createSelector(
  selectSensors,
  (state, props) => props.query,
  (sensors, query) => query ? filterSensors(sensors, query) : sensors
);

// 组件内使用
filterSensors(): void {
  if (this.searchQuery) {
    this.filteredSensors$ = this.store.select(selectFilteredSensors, { query: this.searchQuery });
  } else {
    this.filteredSensors$ = this.store.select(selectSensors);
  }
  this.setPages();
}

方案3:限制订阅执行次数(不推荐,会覆盖原始数据)

如果一定要通过修改store状态实现过滤,需用take(1)确保订阅仅执行一次,打破循环:

import { take } from 'rxjs/operators';

filterSensors(): void {
  if (this.searchQuery !== undefined){
    this.sensors$.pipe(take(1)).subscribe(sensors => {
      const result = filterSensors(sensors, this.searchQuery)
      this.store.dispatch(new SensorsActions.SetSensors(result))
    })
    this.setPages()
  }
}

注意:此方式会覆盖store中的原始传感器数据,再次过滤只能基于上次结果,无法恢复原始数据,除非重新从服务器拉取。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 18:31:02