angularngrxngrx-reducers

Registering reducer in app.module shows error in angular ngrx state management


I am creating an application in Angular 12 and the latest NgRx library for turorial purposes. I have the below model, action, reducer, and app state class.

Model

export interface Tutorial {
  name: string;
  url: string;
}

Actions

import { Injectable } from '@angular/core'
import { Action } from '@ngrx/store'
import { Tutorial } from '../_models/tutorial.model'

export const ADD_TUTORIAL       = '[TUTORIAL] Add'
export const REMOVE_TUTORIAL    = '[TUTORIAL] Remove'

export class AddTutorial implements Action {
    readonly type = ADD_TUTORIAL

    constructor(public payload: Tutorial) {}
}

export class RemoveTutorial implements Action {
    readonly type = REMOVE_TUTORIAL
    
    constructor(public payload: number) {}
}

export type Actions = AddTutorial | RemoveTutorial

Reducer

import { Action } from '@ngrx/store'
import { Tutorial } from '../_models/tutorial.model';
import * as TutorialActions from './../_actions/tutorial.actions'

const initialState: Tutorial = {
    name: 'Initial Tutorial',
    url: 'http://google.com'
}

export function reducer(state: Tutorial[] = [initialState], action: TutorialActions.Actions) {
    switch(action.type) {
        case TutorialActions.ADD_TUTORIAL:
            return [...state, action.payload];
        default:
            return state;
    }
}

App State

import { Tutorial } from "./_models/tutorial.model";

export interface AppState {
    readonly tutorial: Tutorial[];
}

Now I would like to import the reducer in the app.module.ts file. Here is the code:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { reducer } from './_reducers/tutorial.reducer';
import { ReadComponent } from './read/read.component';
import { CreateComponent } from './create/create.component';

@NgModule({
   declarations: [
      AppComponent,
      ReadComponent,
      CreateComponent
   ],
   imports: [
      BrowserModule,
      StoreModule.forRoot({ tutorial: reducer }),
      AppRoutingModule,
   ],
   providers: [],
   bootstrap: [
      AppComponent
   ]
})
export class AppModule { }

But in the app.module.ts file, importing reducer is causing an error. The error is shown below:

Error: src/app/app.module.ts:19:29 - error TS2322: Type '(state: Tutorial[] | undefined, action: Actions) => Tutorial[]' is not assignable to type 'ActionReducer<Tutorial[], Action>'. Types of parameters 'action' and 'action' are incompatible. Type 'Action' is not assignable to type 'Actions'. Property 'payload' is missing in type 'Action' but required in type 'RemoveTutorial'.

19 StoreModule.forRoot({ tutorial: reducer }), ~~~~~~~~

src/app/_actions/tutorial.actions.ts:20:17 20 constructor(public payload: number) {} ~~~~~~~~~~~~~~~~~~~~~~ 'payload' is declared here.

enter image description here

As I am new in NgRx, I don't understand why the problem is happening. Please help me to solve the problem.


Solution

  • It's been a while since I used that syntax it it might be a regression bug. You can assert it to an ActionReducerMap

    StoreModule.forRoot({ tutorial: reducer } as ActionReducerMap<any,any>)
    

    Also, I would suggest to take a look at the createAction and createReducer syntax - it makes things simpler.