The first approach is wrong because as you said, that's an infinite loop.
The second one I think its the best way "to increase the value of a ReplaySubject": it's getting just 1 single value (the current), and then incrementing it just once.
If you want to have a "signal" that triggers that, you could just wrap it around a Subject:
const performIncrement$ = new Subject<number>();
performIncrement$.pipe(
withLatestFrom(pointsCollected$),
map(([increment, v]) => v + increment)
).subscribe(newValue => pointsCollected$.next(newValue))
// Call performIncrement$ every time you want to increment
performIncrement$.next(10)
But in my opinion it's better to declare observables reactively, avoid ReplaySubjects/BehaviourSubjects as much as posible - it's a bit challenging first because instead of thinking the imperative way of "when this happens I need to increase pointsCollected$ by 10" you need to think the other way around "the value of pointsCollected$ is how many times this has happened multiplied by 10". This example would become something like:
const performIncrement$ = new Subject<number>();
const pointsCollected$ = performIncrement$.pipe(
scan((acc, increment) => acc + increment, 0),
startWith(0)
);
If you have multiple subscribers and want to share the same value, you can use a shareReplay(1)
to keep the latest value - Similar to a ReplaySubject but without having to .next
on it.
And ideally you wouldn't have performIncrement$
as a Subject either, it would be another observable that emits whenever that should happen based on your logic.