ThatKidMike
ThatKidMike

Reputation: 336

Angular ngrx/store - why does subscribe method on observable returns previous state?

The problem appears to be beyond me. I have built store correctly and now I can't retrieve current element from the store.

showDialog(id_clicked) {
    this.id = id_clicked
    this.store.dispatch(new action.loadExampleAction(this.id));
    this.observable$ = this.store.select(selectors.getObject);
}

With this simple example I am updating the "observable" whenever button is clicked, dispatching the new action with currently selected id. Showing this.observable$ in the console shows correctly the currently selected object in its payload. But now when I do this:

tmp: <Object> = null
showDialog(id_clicked) {
        this.id = id_clicked
        this.store.dispatch(new action.loadExampleAction(this.id));
        this.observable$ = this.store.select(selectors.getObject);

        this.observable$.subscribe(object => {
        this.tmp = object
        }, first())
        console.log(this.tmp)
}

The tmp inside subscribe returns previous state. I.e. if I click the object with id = 1 at first it will return "undefined", then when I click the object with id = 2 then it returns the object with id = 1 and so on. Even though the console.log on this.observable$ shows that its payload (value) is correct. I am not sure what is going on. I can't simply retrieve current value from this observable. If anything more about it is needed I'll submit additional info.

Upvotes: 3

Views: 2226

Answers (2)

Experimenter
Experimenter

Reputation: 2478

Not sure but it seems like you try to get only the first value that come up in subscription by first().

The problem that NgRx use memoization for selectors and can return previous value if state for this selector doesn't change yet. So when you subscribe it's return init value undefined, after the click you set the state to "1", but you didn't get it because you take only the first value in subscription.

Also clicking is async operation so when you click second time on the next object the new subscription created and immediately return the value stored in the store, it's 1, you see it on the screen and only after that because of "click" async nature the new value in the store become "2", but you didn't see it until click next object and see old value.

As the solution you can remove first() option in your code but than you will get two values, previous and next one, the current. To avoid it you can try to use "Resetting Memoized Selectors" by release(); function from the documentation and than filter the 'null' values in pipe like that:

store
  .pipe(
    select(selectValues),
    filter(val => val !== null)
  )
  .subscribe(/* .. */);

Upvotes: 0

Will Alexander
Will Alexander

Reputation: 3571

You should avoid breaking the Observable chain wherever possible, and also avoid using local variables. Don't use your tmp variable and keep the observable$ instead, and instantiate it in ngOnInit(). Then, wherever you need to consume observable$, subscribe to it appropriately (generally using the async pipe):

ngOnInit() {
  this.observable$ = this.store.select(selectors.getObject);
}

showDialog(idClicked: string) {
  this.store.dispatch(new action.loadExampleAction(this.id));
}

Later, in a template somewhere:

<ng-container *ngIf="observable$ | async as myStuff">
</ng-container>

Upvotes: 1

Related Questions