2
votes

I'm finding that my reducer function does not fire when I dispatch an action to my store

I have a feature module with actions that work fine

The issue I'm having is with the layout state which is the only state I'm registering with the root state.

This is how I'm initialising the app:

//Root
import * as fromLayout from '../core/store/reducers/layout.reducer';
import * as layoutActions from '../core/store/actions/layout.actions';
import { ActionReducerMap, createSelector } from '@ngrx/store';
import { ActionReducer } from 'ngx-bootstrap/mini-ngrx';
//Interface to hold all states, top level state interface is just a map of keys to inner state types.
export interface Root {
  readonly layout: fromLayout.State
}
export type RootActions =  layoutActions.Actions;

//Get reducers
export const initialState = {
  layout: fromLayout.reducer(undefined, {} as layoutActions.Actions)
}

export interface State {
    root: Root;
  }
//Make sure below is not a function expression
  export function rootReducer(state: Root = initialState, action: RootActions){
    return {
        layout: fromLayout.reducer(state.layout, {} as layoutActions.Actions)
    };
  };

  export const reducers: ActionReducerMap<State> = {
    root: rootReducer
  };

In the app.module.ts

  import { reducers } from './reducers/';//This points to -> const reducers: ActionReducer

 StoreModule.forRoot(reducers),
    StoreDevtoolsModule.instrument({maxAge: 25}),
    EffectsModule.forRoot([])

My layout.actions.ts

import { Action } from '@ngrx/store';

export const OPEN_SIDENAV = '[Layout] Open Sidenav';
export const CLOSE_SIDENAV = '[Layout] Close Sidenav';
export const CURRENT_BLOG_ID = '[Layout] Current Blog ID';
export class OpenSidenav implements Action {
  readonly type = OPEN_SIDENAV;
}

export class CloseSidenav implements Action {
  readonly type = CLOSE_SIDENAV;
}

export class CurrentBlogID implements Action {
  readonly type = CURRENT_BLOG_ID;
  constructor(public payload:string){
    console.log(this.payload)
  }
}

export type Actions = OpenSidenav | CloseSidenav | CurrentBlogID;

layout.reducers.ts

import * as layout from '../actions/layout.actions';

export interface State {
  showSidenav: boolean;
  currentBlogID:string
}

const initialState: State = {
  showSidenav: false,
  currentBlogID:''
};

export function reducer(state = initialState, action: layout.Actions): State {
  switch (action.type) {
    case layout.CLOSE_SIDENAV:
      return {
        ...state,
        showSidenav: false,
      };

    case layout.OPEN_SIDENAV:
      return {
        ...state,
        showSidenav: true,
      };
      case layout.CURRENT_BLOG_ID:
      console.log(action.payload)
      return handleCurrentBlogID(state, action)
    default:
      return state;
  }
}

function handleCurrentBlogID(state, action){
  console.log(action.payload)
  let newState = {
    ...state,
    currentBlogID: action.payload,
  }
  return newState
}
export const getShowSidenav = (state: State) => state.showSidenav;

How I'm dispatching to the store:

import { State } from '../../reducers';//This is root state
export class HomeComponent {
  constructor(private store: Store<State>) {}

  onBlogSelected(id){

    this.store.dispatch(new CurrentBlogID(id))
  }
}
2

2 Answers

1
votes

In rootReducer you are calling the layout reducer and passing its return value not function itself. It should be like below

return {
    layout: fromLayout.reducer
};
0
votes

Thanks@Hikmat Gurbanli I've marked your answer as correct but changed my code to deal with a couple of issues

First I actually found it's a little too verbose having the app set up in that way with the Root state.

Second, I was finding that; the feature state wasn't being attached to the Root state, but outside of it

Root->layout.state
featureState

Code:

export interface State {
    readonly layout: fromLayout.State
}

export const reducers: ActionReducerMap<State> = {
    layout: fromLayout.reducer
};

export const initialState = {
    layout: fromLayout.reducer(undefined, {} as layoutActions.Actions)
}

export const getRoot = (state : State) => state;

export const getBlogsState = createSelector(getRoot, (state : State) => state);

Now when I log to the console I get

    layout.state
    featureState