Search code examples
angularrxjsngrx

Dispatch NgRx actions only when some properties changes on Rxjs Polling


I have a Rxjs Polling effect like so :

updateProductData$ = createEffect(() =>
  this.actions$.pipe(
    ofType(fromActions.loadProduct),
    switchMap(_) =>
      this.http.get('endpoint').pipe(
        delay(1000),
        repeat(),
        switchMap((data) => [
          fromActions.updateFoo({foo: data.foo}),
          fromActions.updateBar({bar: data.bar}),
        ])
      )
    )
  );

How can I dispatch updateFoo and updateBar only when data.foo or data.bar change respectively?

I can improve this by using distinctUntilChanged, doing so the actions wont trigger if data.stuff changes, however, both actions still dispatch when either one changes.

...
     repeat(),
     distinctUntileChanged((prev, curr) => prev.foo === curr.foo && prev.bar === curr.bar) // works but it fires both actions when either one changes
     switchMap((data) => [
       fromActions.updateFoo({foo: data.foo}),
       fromActions.updateBar({bar: data.bar}),
     ])

I want to dispatch updateFoo when data.foo changes and updateBar when data.bar changes, knowing that data has a lot of other properties that can change as time goes on.


Solution

  • I think this could be an approach:

    updateProductData$ = createEffect(() =>
      this.actions$.pipe(
        ofType(fromActions.loadProduct),
        switchMap(_) =>
          this.http.get('endpoint').pipe(
            delay(1000),
            repeat(),
            
            multicast(
              new Subject(),
              source => merge(
                // concerning `foo`
                source.pipe(
                  distinctUntilChanged((prev, crt) => prev.foo === crt.foo),
                  map(data => fromActions.updateFoo({foo: data.foo})),
                ),
    
                // concerning `bar`
                source.pipe(
                  distinctUntilChanged((prev, crt) => prev.bar === crt.bar),
                  map(data => fromActions.updateBar({bar: data.bar})),
                ),
              )
            )
          )
        )
      );
    

    The source from multicast's second argument is the instance of the Subject which has been declared as the first argument. By using multicast, we can divide the problem into 2 other smaller problems, without redundantly subscribing to the source(that's why a Subject has been used).