在 Angular 中使用 Ngrx 实现 Store 时遇到错误
2021-07-13
815
请通过分析以下代码来帮助解决错误。我对 Angular 以及 Ngrx 概念非常陌生。另外,为了以防万一,还附上了错误的屏幕截图。
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;
}
}
我我进入的
app.reducer.ts
如下:
{
"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"
}
]
}
1个回答
您的 Reducer 是一个接受两个值并返回状态的函数,但由于您使用
ActionReducerMap<T>
装饰了您的应用,这意味着您应该提供
ActionReducer<>
而不是普通函数。
解决方案 1
只需从您的 appReducer 中删除
ActionReducerMap<T>
即可。
请注意,NgRx 开始改变您编写状态、操作、效果、选择器和 Reducer 的方式。声明状态的较新方法是使用
ActionReducerMap
这里有一篇关于该主题的好文章
(查看第二个解决方案)
解决方案 2 更新您的 ngrx 状态文件以使用 NgRx 提供的新方法,如下所示:
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)
}
Ayman Ali
2021-07-13