Search code examples
angularreduxrxjsngredux

How to get Observable that emits only when a property value changes in an array of objects in redux state?


I have redux state in angular app:

export interface UIAction {
  name: string;
  isActive: boolean;
}
    
export interface IUIState {
  actions: UIAction[];
}

I have built an angular service that subscribes to the state and stores Subjects for each action in a Map and returns that subject for callers that are interested in isActive-value for a certain action. It's something like this:

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

Now I want to change the code and get rid of the Map with all the Subjects and do something like this:

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' */
   )));
}

How this should be done? Below an example.

Initially the UIState.actions looks like this:

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

At this point I call

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

then I dispatch a redux state change (only new values here):

{ name: 'Cancel', isActive: false }

The UIState.actions changes to:

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

The subscription however does not write to console because it wasn't subscribed to changes of 'Cancel' action.

Then I dispatch another redux state change (only values here):

{ name: 'Save', isActive: true }

And the UIState.actions changes to:

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

Now the subscription writes to console:

Value: true

because it was subscribed to changes of 'Save' action.


Solution

  • If someone is interested in this, I did it like this. Thanks for others pointing me to right direction:

    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));
    }