Search code examples
angularreduxngxs

NGXS Devtools doesn't represent the current state


So I started using NGXS and I have some difficulties to inspect my store via Redux Devtools

NGXS has devtools package that integrates with Redux Devtools Extension.

I've installed the latest version of the package (^3.2.0) and imported it into AppModule imports:

imports: [
  NgxsModule.forRoot([ProjectState, AuthState], {developmentMode: true}),
  ...
  NgxsReduxDevtoolsPluginModule.forRoot(),
]

On my AuthState, I did:

export class AuthState implements NgxsOnInit {

    constructor(private auth: AuthService) {}

    ngxsOnInit({dispatch}: StateContext<AuthStateModel>) {
      console.log('State initialized, now getting auth');
      dispatch(CheckAuthSession);
    }

    @Action(CheckAuthSession)
    checkSession({patchState}: StateContext<AuthStateModel>) {
      return this.auth.user$.pipe(
        tap((user) => patchState({initialized: true, user: user})),
      );
    }

When I open the console on Google Chrome, I do get the line

console.log('State initialized, now getting auth')

And the Auth Properties are loaded. Although, on Redux Devtools, I see only:

Redux Devtools

As you can see:

  • The action CheckAuthSession isn't dispatched
  • auth.initialized remains false even when I console log it, it's true.

The strange thing is that when I dispatch a new action, The extension does mention it and the state changes to what it was before the action. I'm not sure if that the correct behavior.

Update 1

When I add take(1) pipe before the tap in checkSession method, it somehow works. I have no idea why.


Solution

  • The reason I didn't see my Action on the Devtools was that the action did never end since I didn't subscribe from it. In other words - if you have an action that returns an observable, it won't be displayed on Devtools until the subscription is being complete.

    In my example, I didn't want to complete the subscription of my action handler, because I needed to listen to every change of my user$.

    So to make my code reasonable, I kept the method unsubscribed, but I made a few changes:

    • I removed patchState({initialized: true, user: user}) from checkSession because then I wouldn't have any idea if the state has changed since the action won't be displayed because it never ends.
    • I added tap((user) => dispatch(new UpdateAuth(user)) inside the pipe of the user$ inside the checkSession method.
    • I created a new method called onUpdateAuth(user: User) which being called when UpdateAuth action is being triggered

    like so:

    @Action(UpdateAuth)
    onUpdateAuth({patchState}: StateContext<AuthStateModel>, {user}: UpdateAuth) {
        patchState({user});
    }
    

    Took some time to figure it out, but after all - it makes much more sense like that.