MichaelAttard
MichaelAttard

Reputation: 2158

@ngrx/store Ignore first emitted value

store.select() emits previous store state.

Is it possible to subscribe to changes from "this point forward" without getting the previous store value?

Upvotes: 13

Views: 8795

Answers (3)

noamyg
noamyg

Reputation: 3104

Just sharing my thoughts (and solution) after reading @Niz's answer.

This is a perfect, practical example of how to utilize the difference between null and undefined. When you initialize your state with null, you're basically saying:

I don't care about differentiating the nullable future state from the initial one. I don't care if the user is null because he has signed out or because he just didn't sign in

However, in some cases this could be insufficient. Think about a case when you need an asynchronous call (implemented in effects) in order to know if you have an active user session. Based on the selection result, you should determine whether to show a login modal or redirect to a content page. With initial user state set to null, you'd pop up that modal and then immediately hide it when that asynchronous call returns a session value.

With initial state set to undefined you can make that differentiation, saying:

Initially, I know nothing about my state, then it's undefined. When I know it should be empty, then I'll set it to null.

Therefor, as a practical solution, I set everything on the app's initialState to undefined. In the example above, I need to know if the login modal should be displayed after the asynchronous call resolves. skipWhile(val => val === undefined) will do the job for sure, but repeating it over and over again feels a little tedious. Plus, it's not really descriptive to our use case. I created a rxjs-custom-operators.ts with a shortened implementation:

import { Observable } from "rxjs";
import { skipWhile } from "rxjs/operators";

export const skipInitial = () => {
  return <T>(source: Observable <T>): Observable<T> => {
    return source.pipe(skipWhile(value => value === undefined));
  };
};

Usage:

navigateOnLoad(): void {
    this.store.pipe(select(selectAuthUser), skipInitial()).subscribe((authUser: CognitoUser) => {
        // Navigate to login if !authUser, else navigate to content...
    });
}

Upvotes: 3

Niz
Niz

Reputation: 506

skip operators need piping now, you can use skip like this:

store.pipe(select(...), skip(1));

In terms of the 'hacky' part, it is a standard practice in ngrx to set an initial state with properties set to null. and that value gets emitted initially. so the first value you get will be null in these cases.

Alternatively you could also consider skipwhile(https://www.learnrxjs.io/learn-rxjs/operators/filtering/skipwhile) and use it like this:

store.pipe(select(...), skipWhile(val => val === undefined));

where undefined is the initial value of the property you are interested in. Rather than setting the initial value of the property to undefined, you could use null as the initial value as well, and change the above skipwhile() accordingly.

Upvotes: 7

cartant
cartant

Reputation: 58420

If you are not interested in the first emitted value, you should be able to use the skip operator:

store.select(...).skip(1)...

Upvotes: 14

Related Questions