Ngrx中日期范围订阅在其他无关状态变更时意外触发是什么原因?
问题原因
- NgRx 中任意 action 派发后,对应的状态切片只要发生修改,就会返回一个全新的状态对象引用。你派发
ResetPageIndex这类 action 时,虽然没有修改start和end属性,但整个 transaction 状态切片的引用已经发生了变更,会触发所有依赖该状态切片的选择器重新执行。 - 你当前的
selectStartAndEndDate选择器的投影函数每次执行都会返回一个新的对象字面量{ start: state.start, end: state.end },哪怕start和end的值完全没有变化,这个返回对象的引用也是全新的。而 NgRx 选择器默认使用引用相等判断返回值是否发生变化,所以每次选择器重新执行都会认为产生了新值,进而触发订阅回调。
解决方案
有三种常用的修复方案,你可以根据自己的项目情况选择:
- 方案1:给选择器添加自定义相等比较器
你可以在创建选择器时指定结果相等检查逻辑,直接对返回对象的内容做对比,而不是只对比引用。如果不想引入第三方工具库,也可以自行实现日期比较逻辑替换示例中的isEqual:import { createSelector, defaultMemoize } from '@ngrx/store'; import { isEqual } from 'lodash-es'; export const selectStartAndEndDate = createSelector( selectTransactionState, (state) => ({ start: state.start, end: state.end }), { memoize: defaultMemoize, resultEqualityCheck: isEqual } ); - 方案2:拆分选择器后组合使用
把start和end拆成两个独立的选择器,再用combineLatest组合,只有当两个属性本身的值发生变化时才会触发回调:// 选择器定义 export const selectStartDate = createSelector(selectTransactionState, state => state.start); export const selectEndDate = createSelector(selectTransactionState, state => state.end); // 组件内使用 combineLatest([ this.store.select(selectStartDate), this.store.select(selectEndDate) ]).subscribe(([start, end]) => { this.range.setValue({ start, end }); }); - 方案3:订阅时添加去重操作符
如果你不想修改选择器定义,可以直接在订阅的流中添加distinctUntilChanged操作符手动去重:import { distinctUntilChanged } from 'rxjs'; import { isEqual } from 'lodash-es'; this.subs.sink = this.store .select(fromTransactionReducer.selectStartAndEndDate) .pipe(distinctUntilChanged(isEqual)) .subscribe((date) => { console.log("range", date); this.range.setValue({ start: date.start, end: date.end, }); });
内容的提问来源于stack exchange,提问作者walexy
相关产品推荐
相关产品推荐

