Akita: Query select fires twice returning the previous value in the second time

Created on 17 Feb 2020  路  6Comments  路  Source: datorama/akita

I'm submitting a...


[*] Support request
[*] Bug report ??

Current behavior

In this stackblitz I have a simple user entityStore with activeState and a filter property:

export interface UserState extends EntityState<User>, ActiveState { 
    filter: string;
}

let initialState: UserState = {
    active: null,
    filter: '',
}

in AppComponent I get users from api and display it. And I filter user's username property via a FormControl

in Component2 I display a json of the selected user

the problem here is:

In Component2, when I select activeId I got notified 2 times, first with the correct value, then by the previous value (you can see the log in the console)

this.userQuery.selectActiveId().subscribe( id => {
    console.log(id) // logged twice and changed to previous value
    this.user = this.userQuery.getEntity(id);
})

I couldn't determine the cause so if you please help me figure it out.

Expected behavior

select property fire only once

Minimal reproduction of the problem with instructions

https://stackblitz.com/edit/angular-ngqgr3

Most helpful comment

Let me give you a tip. It's not nice to say to an author of open-source library things like " I was just about to switch temporarily back to ngrx till this issue get fixed."

You're welcome to submit a PR and add the same functionality to the remove method.

All 6 comments

```ts
// AppComponent
this.userService.get().subscribe();

this.userQuery.selectUsers$.subscribe(users => {
const active = users.length ? users[0].id : null;
this.userService.updateActiveId(active);
});

// Comp B
this.userQuery.selectActiveId().subscribe( id => {
console.log(id);
})
```
Ok, so here's what's happening:

App starts:

  • selectUsers$ emits synchronously, and updates active to null.
  • selectActiveId emits synchronously with null.

So far, so good.

userService.get() returns:

  • It calls store.set() which changes active to null, and updates the users.
  • selectUsers$ emits synchronously with the users and update active to the first user id.
  • selectActiveId emits synchronously and recivies the new active id.

But now we have a problem. The selectActiveId subscriber still needs to get the emission fired as a result of the store update, which changed the active to null. That's why you see: null, id, null

You have two options:

  • Add a delay(0) to selectUsers$
  • Move the active update to the service:
    return this.http
      .get<User[]>("https://jsonplaceholder.typicode.com/users")
      .pipe(
        tap(users => {
          applyTransaction(() => {
            this.userStore.set(users);
            let found: boolean = !!users.find(
              o => o.id == this.userQuery.getActiveId()
            ); 
            let id: number = users.length
              ? found
                ? this.userQuery.getActiveId()
                : users[0].id
              : null;
            this.updateActiveId(id);
          });
        })
      );

I have added in the latest version the option to pass the active id to set:

this.userStore.set(users, { activeId: users[0].id });

Thanks. This issue was hanging for a while. It recurred several time with in my project with different forms. It drove me crazy for not being able to determine the cause. I was just about to switch temporarily back to ngrx till this issue get fixed.

The delay(0) solution is good. It defers the execution from synchronous to asynchronous and problem solved.

I think I'll stick to delay(0) solution as it fix another issue with

userStore.remove(id);

like you did in this fix:

I have added in the latest version the option to pass the active id to set:

this.userStore.set(users, { activeId: users[0].id });

I think you should add similar fix to store.remove(id) to update the next active id

  this.userStore.remove(id, { activeId: nextId });

Let me give you a tip. It's not nice to say to an author of open-source library things like " I was just about to switch temporarily back to ngrx till this issue get fixed."

You're welcome to submit a PR and add the same functionality to the remove method.

I don't think there a competition between state management libraries here.
But let me tell you, if you take it this way, and if it calms you down: with continuous support, Akita will outrun other libraries for sure, and it should be the official @angular/state library.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hoisel picture hoisel  路  5Comments

zvnt picture zvnt  路  6Comments

simeyla picture simeyla  路  7Comments

eitanfr picture eitanfr  路  4Comments

DanielNetzer picture DanielNetzer  路  5Comments