Search code examples
ngrx

ngRx state change getting lost


Following some examples, I'm trying to manage the state of a sideBar component (open/closed). I have defined some interfaces to manage my state objects:

export interface AppState {
  readonly layoutState: LayoutState;
}

export interface LayoutState {
  sideBarOpen: boolean;
}

I have a local variable in my Component that implements the LayoutState interface: layout: LayoutState;

I subscribe to my state in ngOnInit:

this.store.subscribe(appState => {
  this.layout = appState.layoutState;
  // (5)
});

I have a button in my UI that calls a function that contains this line: this.toggleSideNav(this.layout.sideBarOpen);

toggleSideNav looks like this:

toggleSideNav(currentState: boolean) {
    this.store.dispatch(new LayoutActions.ToggleSidebarAction(currentState)); // (1)
  }

Here is my effect:

@Effect()
  toggleSidebar$ = this.actions$
    .ofType(LayoutActions.TOGGLE_SIDEBAR)
    .switchMap((action: ToggleSidebarAction) =>
      this.layoutService.toggleSidebar(action.payload) // (2)
    )
    .map((layoutState: LayoutState) => {
       // (4)
      return new LayoutActions.ToggleSidebarSuccessAction(layoutState);
    });

Here is the layoutService.toggleSidebar function:

toggleSidebar(currentState: boolean): Observable<LayoutState> {
    const rv = {
      sideBarOpen: !currentState
    };
    // (3) 
    return Observable.of(rv);
  }

Here is what I see when debugging. At position:

(1) - currentState is false - as expected

(2) - action.payload is false - as expected

(3) - rv is {sideBarOpen: true} - as expected

(4) - layoutState is {sideBarOpen: true} - as expected

(5) - this.layout is {sideBarOpen: false} - !!

Why does the state get "lost" between (4) and (5)?


Solution

  • I was forgetting that after the Effect, it goes to the Reducer. My Reducer only had this:

    switch (action.type) {
        case Actions.LOAD_LAYOUT_SUCCESS:
          return action.payload;
        default:
          return state;
      }
    

    I had to add a case for the TOGGLE_SIDEBAR_SUCCESS message to send the new state (contained in the payload):

    switch (action.type) {
        case Actions.LOAD_LAYOUT_SUCCESS:
          return action.payload;
        case Actions.TOGGLE_SIDEBAR_SUCCESS:   <--
          return action.payload;               <--
        default:
          return state;
      }
    

    Before adding that, the reducer was falling through to the default case, and returning state, which is the old state.