如何获得仅在 redux 状态的对象数组中的属性值更改时才发出的 Observable?

Posted

技术标签:

【中文标题】如何获得仅在 redux 状态的对象数组中的属性值更改时才发出的 Observable?【英文标题】:How to get Observable that emits only when a property value changes in an array of objects in redux state? 【发布时间】:2021-10-14 07:44:20 【问题描述】:

我在 Angular 应用中有 redux 状态:

export interface UIAction 
  name: string;
  isActive: boolean;

    
export interface IUIState 
  actions: UIAction[];

我已经构建了一个 Angular 服务,它订阅状态并将每个操作的主题存储在 Map 中,并为对某个操作的 isActive-value 感兴趣的调用者返回该主题。是这样的:

public getIsActive$(name: string): Observable<boolean>   
  let sub = this.subsMap.get(name);
  return sub.asObservable();

现在我想更改代码并删除包含所有主题的地图并执行以下操作:

public getIsActive$(name: string): Observable<boolean>   
  return this.ngRedux.select<IUIState>('ui').pipe(
  /* this should use some operators so that
     the returned Observable emits only when 
     UIAction.isActive changes for the action 
     in question. The action in question is
     found from the state.actions-array using 
     parameter 'name' */
   )));

这应该怎么做?下面是一个例子。

最初 UIState.actions 如下所示:

[  name: 'Save', isActive: false ,  name: 'Cancel', isActive: true  ]

此时我调用

myService.getIsActive$('Save').subscribe(isActive => 
  console.log('Value : $isActive);
);

然后我发送一个 redux 状态更改(这里只有新值):

 name: 'Cancel', isActive: false 

UIState.actions 更改为:

[  name: 'Save', isActive: false ,  name: 'Cancel', isActive: false ]

但订阅不会写入控制台,因为它没有订阅“取消”操作的更改。

然后我发送另一个 redux 状态更改(这里只有值):

 name: 'Save', isActive: true 

UIState.actions 变为:

[  name: 'Save', isActive: true,  name: 'Cancel', isActive: false ]

现在订阅写入控制台:

Value: true

因为它订阅了“保存”操作的更改。

【问题讨论】:

或许distinctUntilChanged运营商可以帮到你 我自己发现了它并认为我明白了 由于您正在处理对象,distinctUntilChanged() 将不起作用,因为每个对象引用都是唯一的。使用distinctUntilKeyChanged('isActive),因为它将运行此特定对象属性的条件。 我故意写了distinctUntilChanged,因为你可以指定一个比较函数,我不认为distinctUntilKeyChanged会帮助他,因为它是一个对象数组 【参考方案1】:

希望我正确理解了这个要求。对于更复杂的情况,您可以将自定义函数应用到 distinctUntilChanged()

public getIsActive$(name: string):Observable<boolean> 
  return this.ngRedux.select<IUIState>('ui').pipe(
    map(uiState=>uiState.find(state=>state.name===name)),
    filter(state=>!!state),
    distinctUntilChanged((prev, curr)=>
      prev.name===curr.name && prev.isActive === curr.isActive
    ),
    map(state=>state.isActive)
  );

这里逐行说明正在发生的事情:

根据名称查找数组中的状态 过滤出.find()返回undefined 应用一个自定义函数来检查nameisActive。如果任一属性与其先前的值不匹配,它将发出新值。 现在我们有了新值,返回 isActive 布尔值。

【讨论】:

感谢您的回答,但包含项目的数组称为操作。因此必须从 uiState.actions 中搜索有问题的项目。这在“注释代码”中说。 我更新了我的问题以更好地解释我需要什么【参考方案2】:

如果有人对此感兴趣,我会这样做。感谢其他人指出我正确的方向:

public getIsActive$(name: UIActionName): Observable<boolean> 
  return this.getUIAction$(name).pipe(
    map(axn => axn.isActive)).pipe(distinctUntilChanged());


private getUIAction$(name: UIActionName): Observable<UIAction> 
  return this.ngRedux.select<IUIState>('ui').pipe(map(state => 
    return state.actions.find(actn => actn.name === name);
  )).pipe(
    filter((axn: UIAction | undefined): axn is UIAction => axn !== undefined));

【讨论】:

以上是关于如何获得仅在 redux 状态的对象数组中的属性值更改时才发出的 Observable?的主要内容,如果未能解决你的问题,请参考以下文章

在redux状态下更新嵌套在另一个数组中的数组属性的问题

如何在redux中更新特定数组项内的单个值

如何在 ReactJs Redux 中删除/设置状态数组的“null”嵌套属性?

如何使用 AngularJs 对对象数组中的属性值求和

更新深层不可变状态属性时,Redux 不更新组件

Redux reducer,检查状态数组中是不是存在值并更新状态