'Create one subscription for multiple 'observable consumers'

To manager state there is this store service for users. In the store there is a 'select' method which maps to get the users id's:

type User = { userId: number };

@Injectable({ providedIn: "root" })
export class SomeService {
  activeUsers$ = new BehaviorSubject<User[]>([]);

  selectUserIds = (): Observable<number[]> => {
    return this.activeUsers$
      .asObservable()
      .pipe(map((data) => data.map(({ userId }) => userId)));
  };
  // ...
}

In the component I am calling that method to see which user is active:

@Component({
  selector: "app-root",
  template: `
  <div>
    <h1>See console output</h1>
    <ng-container *ngFor="let user of users">
      <div [ngClass]="{'active': (activeUserIds$ | async).includes(user.userId)}">
        {{ user.userName }}
      </div>
    </ng-container>
  </div>`,
})
export class AppComponent implements OnInit {

  activeUserIds$: Observable<number[]>;

  users = [
    { userId: 1, userName: "John" },
    { userId: 2, userName: "Fred" },
    { userId: 3, userName: "Alex" }
  ];

  constructor(private someService: SomeService) {}

  ngOnInit(): void {
    this.activeUserIds$ = this.someService.selectUserIds().pipe(
      tap((data) => {
        console.log("data: ", data);
      }),
    );
  }
}

But in the console the happens the amount of times it's called in the template (three times):

// console output:
// data: []
// data: []
// data: []

It seems superfluous to have it done three times. But I do want it to react to updates to the state.

My thoughts were that by creating the 'activeUserIds$' in the OnInit I would have 1 observable and use that multiple times. But instead it seems like it's subscribing multiple times.

What would be the way to only have 1 subscription?

Sandbox link: https://codesandbox.io/s/red-bird-n7xips?file=/src/app/app.component.ts



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source