Search code examples
angularrxjsngrx-store

RxJs subscriber isn't updating immediately after store changes


The Project:

I'm building a login component within Angular using NgRx and RxJs.

The Current Setup

Within my login.component.ts file's constructor, I subscribe to the 'login' state.

On click of the 'login' button in the UI, I dispatch an action in the following flow:

GetUser Action > GetUser Effect HTTP request > Success = GetUserSuccess Action > GetUserSuccess Reducer adds the payload (jwt token) to the store, and changes 'isLoggedIn' property to true.

If failure, GetUserFailure Action is dispatched from the effect, GetUserFailure Reducer adds nothing aside from the error message to the store under 'errorMsg' property.

The Desired Next Step

Within login.component.ts, as i've subscribed to the login state, I wish to check to see if the the 'isLoggedIn' is true or false. If true, this.router.navigateByUrl('/dashboard'); otherwise, get the 'errorMsg' from the store and spit that out on the page as a string.

The Problem

I'm dispatching an action within a function that is fired from the login button, but I also want to navigate once the store has been checked. If I click the button once, I can get a console log that states 'isLoggedIn' is false (as when I click the button, it is). However, if I click the button again, it changes to true.

How do I ensure that the login.component.ts file is actively monitoring the store, and routes to 'dashboard' once that value has changed to true? I assumed the following code would do that but apparently not.

The Code

Inside constructor:

this.store.subscribe(state => (this.loginState$ = state));

Inside the loginHandler function that is fired on click of the Login Button:

  loginHandler() {
    const loginCredentials = this.loginForm;

    this.store.dispatch(new GetUser(loginCredentials.value));

    // this.loginState$.isLoggedIn should be true, but it's false 
    // (presumably because it's checking before the HTTP request and reducer has fired)
    console.warn(this.loginState$); 
  }

In Summary

How do I wait to see the store property change, and then route based off of that without using something like set interval?


Solution

  • Thanks to those who answered/helped!

    The fix

    // Subscribe to the store, check if logged in, if so route, else get error message
    this.userStateSubscription = this.store.subscribe(state => {
      this.loginState$ = state;
      if (this.loginState$.login.isLoggedIn) {
        this.router.navigateByUrl('/dashboard');
      } else {
        this.errorMsg = this.loginState$.login.error;
      }
    

    In a nutshell

    Although I subscribed to the store within the constructor and not within OnInit or a function.

    I simply moved the subscription to happen within the login function, expended the function itself to use 'loginState$' locally (just as I was doing before), but then I routed based off of a value within that state.

    As far as I can understand, because I've subscribed to the state OUTSIDE of the constructor, it updated the state immediately as required.

    Also, by assigning the subscription to a const variable I was then able to unsubscribe in NgOnDestroy lifecycle hook to prevent memory leaks.