Please provide some help in resolving the error by analyzing the below code. I am very new to Angular as well as this Ngrx concept. Also attaching the screenshot of error just in case.
package.json
{
"name": "recipe-book",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "~12.1.1",
"@angular/common": "~12.1.1",
"@angular/compiler": "~12.1.1",
"@angular/core": "~12.1.1",
"@angular/forms": "~12.1.1",
"@angular/platform-browser": "~12.1.1",
"@angular/platform-browser-dynamic": "~12.1.1",
"@angular/router": "~12.1.1",
"@ngrx/effects": "^12.2.0",
"@ngrx/router-store": "^12.2.0",
"@ngrx/store": "^12.2.0",
"bootstrap": "^3.4.1",
"jquery": "^3.6.0",
"rxjs": "~6.6.0",
"tslib": "^2.2.0",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~12.1.1",
"@angular/cli": "~12.1.1",
"@angular/compiler-cli": "~12.1.1",
"@types/jasmine": "~3.6.0",
"@types/node": "^12.11.1",
"jasmine-core": "~3.7.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"typescript": "~4.3.2"
}
}
app.reducer.ts
import { ActionReducerMap } from '@ngrx/store';
import * as fromRecipes from '../recipes/store/recipes.reducer';
export interface AppState {
recipes: fromRecipes.State;
}
export const appReducer: ActionReducerMap<AppState> = {
recipes: fromRecipes.RecipesReducer, // "recipes" word has a red line with error
};
recipes.action.ts
import { Action } from '@ngrx/store';
import { Recipe } from '../recipe/recipe.model';
export const FETCH_RECIPES = '[Recipes] Fetch Recipes';
export const SET_RECIPES = '[Recipes] Set Recipes';
export class FetchRecipes implements Action {
readonly type = FETCH_RECIPES;
}
export class SetRecipes implements Action {
readonly type = SET_RECIPES;
constructor(public payload: Recipe[]) {}
}
export type RecipesActions = FetchRecipes | SetRecipes;
recipes.effects.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects';
import * as RecipesActions from './recipes.action';
import { map, switchMap, tap } from 'rxjs/operators';
import { Recipe } from '../recipe/recipe.model';
@Injectable()
export class RecipesEffects {
constructor(private action$: Actions, private http: HttpClient) {}
@Effect()
fetchRecipes = this.action$.pipe(
ofType(RecipesActions.FETCH_RECIPES),
switchMap(() => {
return this.http.get<Recipe[]>(
'https://recipebook-a7054-default-rtdb.firebaseio.com/recipes.json'
);
}),
map((recipes) => {
return new RecipesActions.SetRecipes(recipes);
})
);
}
recipes.reducer.ts
import { Recipe } from '../recipe/recipe.model';
import * as RecipesActions from './recipes.action';
export interface State {
recipes: Recipe[];
}
const initialState: State = {
recipes: [],
};
export function RecipesReducer (
state = initialState,
action: RecipesActions.RecipesActions
) {
switch (action.type) {
case RecipesActions.SET_RECIPES:
return {
...state,
recipes: [...action.payload],
};
default:
return state;
}
}
The error that I am getting in app.reducer.ts is below:
{
"resource": "/f:/E/RecipeBook/src/app/store/app.reducer.ts",
"owner": "typescript",
"code": "2322",
"severity": 8,
"message": "Type '(state: State | undefined, action: RecipesActions) => State' is not assignable to type 'ActionReducer<State, Action>'.\n Types of parameters 'action' and 'action' are incompatible.\n Type 'Action' is not assignable to type 'RecipesActions'.\n Type 'Action' is not assignable to type 'FetchRecipes'.\n Types of property 'type' are incompatible.\n Type 'string' is not assignable to type '\"[Recipes] Fetch Recipes\"'.",
"source": "ts",
"startLineNumber": 9,
"startColumn": 3,
"endLineNumber": 9,
"endColumn": 10,
"relatedInformation": [
{
"startLineNumber": 5,
"startColumn": 3,
"endLineNumber": 5,
"endColumn": 10,
"message": "The expected type comes from property 'recipes' which is declared here on type 'ActionReducerMap<AppState, Action>'",
"resource": "/f:/E/RecipeBook/src/app/store/app.reducer.ts"
}
]
}
Your Reducer is a function that accept two values and return state but since you decorated your app with ActionReducerMap<T>
that's mean you should provide ActionReducer<>
instead of normal a function.
Solution 1
Simply remove ActionReducerMap<T>
from your appReducer and it should work.
note that NgRx start changing the way you write your state,action,effects,selectors and reducer. the newer method of declaring the state is the one that use ActionReducerMap
here is a good article regarding the topic (Look at second solution)
Solution 2 update your ngrx state files to use the new method provided by NgRx like following:
recipes.actions.ts
import { createAction, props } from '@ngrx/store';
import { Recipe } from '../recipe/recipe.model';
let actionTypes = {
FETCH_RECIPES : '[Recipes] Fetch Recipes',
SET_RECIPES : '[Recipes] Set Recipes',
}
export const FetchRecipes = createAction(actionTypes.FETCH_RECIPES);
export const SetRecipes = createAction(actionTypes.SET_RECIPES,props<{recipe:Recipe[]}>());
recipes.effects.ts
//....imports , all the imports will come here
@Injectable()
export class RecipesEffects {
constructor(private action$: Actions, private http: HttpClient) {}
fetchRecipt$ = createEffect(() =>
this.action$.pipe(
ofType(FetchRecipes),
mergeMap(action =>
this.fetchFromUrl().pipe(map(result => SetRecipes({ recipe: result })))
)
)
);
fetchFromUrl() {
return this.http.get<Recipe[]>(
'https://recipebook-a7054-default-rtdb.firebaseio.com/recipes.json'
);
}
}
recipe.reducer.ts
import { createReducer, on,Action } from '@ngrx/store';
import * as RecipesActions from './recipes.actions'
export interface State {
recipes: Recipe[];
}
const initialState: State = {
recipes: [],
};
const reducer = createReducer(initialState,
on(RecipesActions.SetRecipes,(state,payload)=>{
return {...state,recipes:[...payload.recipe]}
})
)
export function RecipesReducer (
state = initialState,
action: Action
) {
return reducer(state,action)
}