How to register multiple NgRx stores in a strict mode Angular application?

Viewed 322

After having created a working store service based on RxJs and observables, I'm now trying to use the NgRx library instead.

The application runs in strict mode and registering the store:

StoreModule.forRoot(
  { soundtracks: soundtrackReducer }
),

presents a compile time error:

Type '(state: SoundtrackState | undefined, action: SoundtrackActions) => SoundtrackState' is not assignable to type 'ActionReducer<SoundtrackState, Action>'.
  Types of parameters 'action' and 'action' are incompatible.
    Type 'Action' is not assignable to type 'SoundtrackActions'.
      Property 'error' is missing in type 'Action' but required in type 'PostFail'.ts(2322)

The actions are defined as:

export enum SoundtrackActionTypes {
  Add = '[Soundtrack] Add',
  PostFail = '[Soundtrack] Post Fail'
}

export class Add implements Action {
  public readonly type = SoundtrackActionTypes.Add;
  constructor(public soundtrack: Soundtrack) { }
}

export class PostFail implements Action {
  public readonly type = SoundtrackActionTypes.PostFail;
  constructor(public error: HttpErrorResponse) { }
}

export type SoundtrackActions = Add | PostFail;

And the reducer:

export interface SoundtrackState {
  data: Array<Soundtrack>;
  isLoading: boolean;
  message: string;
}

const initialSoundtrackState: SoundtrackState = {
  data: [],
  isLoading: false,
  message: ''
};

export function soundtrackReducer(state: SoundtrackState = initialSoundtrackState, action: SoundtrackActions): SoundtrackState {
  switch (action.type) {
    case SoundtrackActionTypes.Add: {
      const updatedData: Array<Soundtrack> = [...state['data']];
      updatedData.push(action.soundtrack);
      return {
        ...state,
        data: updatedData,
        isLoading: false,
        message: ''
      };
    }
    case SoundtrackActionTypes.PostFail: {
      return {
        data: [],
        isLoading: false,
        message: 'The soundtrack could not be posted'
      };
    }
    default: {
      return state;
    }
  }
}

So I reckoned I should have a custom base action:

export interface CustomAction extends Action {
  type: string;
  payload?: any;
  error?: HttpErrorResponse;
}

and use it for a post action as:

export class PostFail implements CustomAction {
  public readonly type = SoundtrackActionTypes.PostFail;
  constructor(public error: HttpErrorResponse) { }
}

But I could not find out how to tell the registering call to consider this custom action.

Other than that, is using a switch construct with classes the right way to create actions ? Should I not instead prefer using helper functions ? Something like:

export const add = createAction(SoundtrackActionTypes.Add, props<{soundtrack: Soundtrack}>());
export const postFail = createAction(SoundtrackActionTypes.PostFail, props<{error: HttpErrorResponse}>());

But then, when creating this reducer:

const _soundtrackReducer = createReducer(initialSoundtrackState,
  on(add, (state, { soundtrack }) => {
    const updatedData: Array<Soundtrack> = [...state['data']];
    updatedData.push(soundtrack);
    return {
      ...state,
      data: updatedData,
      isLoading: false,
      message: ''
    };
  }),
  on(postFail, (state, { error }) => {
    return {
      data: [],
      isLoading: false,
      message: 'The soundtrack could not be posted'
    };
  })
);

export function soundtrackReducer(state: SoundtrackState | undefined, action: SoundtrackActions): SoundtrackState {
  return _soundtrackReducer(state, action);
}

the registering still complains at compile time of a type mismatch, with the same compile time error.

Also, when registering the store module:

StoreModule.forRoot(
  { soundtracks: soundtrackReducer }
),

What if I have multiple feature stores ? And a global preferences store as well ? How can I register all these stores ?

UPDATE: I could resolve the compile time error by removing the union type in the reducer signature:

export function soundtrackReducer(state: SoundtrackState | undefined, action: Action): SoundtrackState {
  return _soundtrackReducer(state, action);
}

Indeed the SoundtrackActions union type was not needed.

0 Answers
Related