无法读取未定义(读取“地图”)NGRX 实体的属性

Posted

技术标签:

【中文标题】无法读取未定义(读取“地图”)NGRX 实体的属性【英文标题】:Cannot read properties of undefined (reading 'map') NGRX Entity 【发布时间】:2022-01-21 11:28:42 【问题描述】:

它发生在 selectAll 选择器上,我查看了其他答案,我正在使用 createfeatureSelector 来调用状态。我已经设法调度动作,将状态保存到商店,但我不能使用选择器。任何帮助将不胜感激。

ERROR TypeError: Cannot read properties of undefined (reading 'map')
    at ngrx-entity.mjs:21
    at ngrx-store.mjs:697
    at memoized (ngrx-store.mjs:578)
    at defaultStateFn (ngrx-store.mjs:601)
    at ngrx-store.mjs:700
    at memoized (ngrx-store.mjs:578)
    at ngrx-store.mjs:697
    at memoized (ngrx-store.mjs:578)
    at defaultStateFn (ngrx-store.mjs:601)
    at ngrx-store.mjs:700

tod​​os.actions.ts

import  Update  from "@ngrx/entity";
import  createAction, props  from "@ngrx/store";
import  Todo  from "../../../models/todo.interface";

export enum TodosActionTypes 
  GET_TODOS = '[todos] get todos',
  GET_TODOS_FAIL = '[todos] get todos fail',
  GET_TODOS_SUCCESS = '[todos] get todos success',
  ADD_TODO = '[todo] add todo',
  EDIT_TODO = '[todo] edit todo',
  DELETE_TODO = '[todo] delete todo',


const getTodos = createAction(TodosActionTypes.GET_TODOS);
const getTodosFail = createAction(TodosActionTypes.GET_TODOS_FAIL, props<error: Error>());
const getTodosSuccess = createAction(TodosActionTypes.GET_TODOS_SUCCESS, props<todos: Todo[]>());
const editTodo = createAction(TodosActionTypes.EDIT_TODO, props<edit: Update<Todo>, completed: boolean>());
const deleteTodo = createAction(TodosActionTypes.DELETE_TODO, props<id: string>());
const addTodo = createAction(TodosActionTypes.ADD_TODO, props<todo: Todo>());

export const TodosActions = 
  getTodos,
  getTodosFail,
  getTodosSuccess,
  editTodo,
  deleteTodo,
  addTodo
;

tod​​os.reducer.ts

import  createEntityAdapter, EntityAdapter, Update  from '@ngrx/entity';
import  createReducer, on  from '@ngrx/store';
import  Todo  from '../../../models/todo.interface';
import  TodosState  from '../../../models/todos-state.interface';
import  TodosActions  from '../actions/todos.actions';

export function selectTodoId(a: Todo): string 
  return a.id;

export function sortByName(a: Todo, b: Todo): number 
  return a.name.localeCompare(b.name);

export const adapter: EntityAdapter<Todo> = createEntityAdapter<Todo>(
  selectId: selectTodoId,
  sortComparer: sortByName,
);

export const initalState: TodosState = adapter.getInitialState(
  loading: false,
  loaded: false,
  error: 
    message: '',
    name: '',
  ,
);

function getTodosHandler(state = initalState) 
  return 
    ...state,
    loading: true,
  ;

function getTodosFailHandler(state = initalState, action:  error: Error ) 
  return 
    ...state,
    loading: false,
    loaded: true,
    error: action.error,
  ;


function getTodosSuccessHandler(
  state = initalState,
  action:  todos: Todo[] 
) 
  return adapter.addMany(action.todos, 
    ...state,
    loading: false,
    loaded: true,
  );

function editTodosHandler(state = initalState, action:  edit: Update<Todo> ) 
  return adapter.updateOne(action.edit, 
    ...state,
  );

function deleteTodoHandler(state = initalState, action:  id: string ) 
  return adapter.removeOne(action.id, 
    ...state,
  );

function addTodoHandler(state = initalState, action: todo: Todo) 
  return adapter.addOne(action.todo, 
    ...state,
  )

export const TodosReducer = createReducer(
  initalState,
  on(TodosActions.getTodos, getTodosHandler),
  on(TodosActions.getTodosFail, getTodosFailHandler),
  on(TodosActions.getTodosSuccess, getTodosSuccessHandler),
  on(TodosActions.editTodo, editTodosHandler),
  on(TodosActions.deleteTodo, deleteTodoHandler),
  on(TodosActions.addTodo, addTodoHandler)
);

tod​​os.selectors.ts

import  createFeatureSelector, createSelector  from '@ngrx/store';
import  TodosState  from '../../../models/todos-state.interface';
import  adapter  from '../reducers/todos.reducers';
import  Todo  from '../../../models/todo.interface';



const  selectIds, selectEntities, selectAll, selectTotal  =
  adapter.getSelectors();

  export const getTodosFeatureState =
  createFeatureSelector<TodosState>('todos');


const getTodosLoading = (state: TodosState): boolean => state.loading;
const getTodosLoaded = (state: TodosState): boolean => state.loaded;

const selectAllTodos = createSelector(getTodosFeatureState, selectAll);


const selectCompletedTodos = createSelector(selectAllTodos, (todos: Todo[]) => todos.filter(todo => todo.completed === true));

const selectTodosLoading = createSelector(getTodosFeatureState, getTodosLoading);

const selectTodosLoaded = createSelector(getTodosFeatureState, getTodosLoaded);

const selectAllTodosIds = createSelector(getTodosFeatureState, selectIds);

const selectAllTodosEntities = createSelector(getTodosFeatureState, selectEntities);

const selectTodosTotal = createSelector(getTodosFeatureState, selectTotal);

export const TodosSelectors = 
  selectAllTodos,
  selectCompletedTodos,
  selectTodosLoading,
  selectTodosLoaded,
  selectAllTodosIds,
  selectAllTodosEntities,
  selectTodosTotal


tod​​os-index.reducer.ts

import  ActionReducerMap  from '@ngrx/store';
import  TodosState  from '../../../models/todos-state.interface';
import * as fromTodosReducer from './todos.reducers';


export interface TodosFeatureState 
  todos: TodosState;


export const reducers: ActionReducerMap<TodosFeatureState> = 
  todos: fromTodosReducer.TodosReducer,
;

tod​​os.effect.ts

import  Injectable  from '@angular/core';
import  Actions, createEffect, ofType  from '@ngrx/effects';
import  catchError, map, switchMap, take,  from 'rxjs';
import  Todo  from '../../../models/todo.interface';
import  TodosService  from '../../../services/todos.service';
import  TodosActions, TodosActionTypes  from '../actions/todos.actions';

@Injectable()
export class TodosEffects 
  constructor(private todosService: TodosService, private actions$: Actions) 
  loadTodos$ = createEffect(() =>
    this.actions$.pipe(
      ofType(TodosActionTypes.GET_TODOS),
      switchMap(() =>
        this.todosService.getTodos().pipe(
          take(1),
          map((todos: Todo[]) => TodosActions.getTodosSuccess( todos )),
          catchError(async (error: Error) =>
            TodosActions.getTodosFail( error )
          )
        )
      )
    )
  );



tod​​osStateModule

import  NgModule  from "@angular/core";
import  EffectsModule  from "@ngrx/effects";
import  StoreModule  from "@ngrx/store";
import  SnackBarService  from "../../services/snackbar.service";
import  TodosEffects  from "./effects/todos.effects";
import * as fromTodos from './reducers/todos-index.reducers';

@NgModule(
  imports: [
      StoreModule.forFeature('todos', fromTodos.reducers),
      EffectsModule.forFeature([TodosEffects]),
  ],
  providers:[SnackBarService]
)
export class TodosNgrxStateModule 

我的 package.json


  "name": "marin-software",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": 
    "ng": "nx",
    "postinstall": "node ./decorate-angular-cli.js && ngcc --properties es2015 browser module main",
    "start": "nx serve",
    "build": "nx build",
    "test": "nx test"
  ,
  "private": true,
  "dependencies": 
    "@angular/animations": "~13.1.0",
    "@angular/cdk": "^13.1.1",
    "@angular/common": "~13.1.0",
    "@angular/compiler": "~13.1.0",
    "@angular/core": "~13.1.0",
    "@angular/forms": "~13.1.0",
    "@angular/material": "^13.1.1",
    "@angular/platform-browser": "~13.1.0",
    "@angular/platform-browser-dynamic": "~13.1.0",
    "@angular/router": "~13.1.0",
    "@ngrx/effects": "^13.0.2",
    "@ngrx/entity": "^13.0.2",
    "@ngrx/store": "^13.0.2",
    "@ngrx/store-devtools": "^13.0.2",
    "@nrwl/angular": "13.3.9",
    "@tailwindcss/forms": "^0.4.0",
    "@tailwindcss/typography": "^0.5.0",
    "rxjs": "~7.4.0",
    "tslib": "^2.0.0",
    "zone.js": "~0.11.4"
  ,
  "devDependencies": 
    "@angular-devkit/build-angular": "~13.1.0",
    "@angular-eslint/eslint-plugin": "~13.0.1",
    "@angular-eslint/eslint-plugin-template": "~13.0.1",
    "@angular-eslint/template-parser": "~13.0.1",
    "@angular/cli": "~13.1.0",
    "@angular/compiler-cli": "~13.1.0",
    "@angular/language-service": "~13.1.0",
    "@nrwl/cli": "13.3.9",
    "@nrwl/cypress": "13.3.9",
    "@nrwl/eslint-plugin-nx": "13.3.9",
    "@nrwl/jest": "13.3.9",
    "@nrwl/linter": "13.3.9",
    "@nrwl/nx-cloud": "latest",
    "@nrwl/tao": "13.3.9",
    "@nrwl/workspace": "13.3.9",
    "@types/jest": "27.0.2",
    "@types/node": "14.14.33",
    "@typescript-eslint/eslint-plugin": "~5.3.0",
    "@typescript-eslint/parser": "~5.3.0",
    "cypress": "^9.1.0",
    "eslint": "8.2.0",
    "eslint-config-prettier": "8.1.0",
    "eslint-plugin-cypress": "^2.10.3",
    "jest": "27.2.3",
    "jest-preset-angular": "11.0.0",
    "prettier": "^2.3.1",
    "tailwindcss": "^3.0.7",
    "ts-jest": "27.0.5",
    "typescript": "~4.4.3"
  


【问题讨论】:

【参考方案1】:

试试这个

export const getFeatureState =
  createFeatureSelector<TodosState>('todos');
export const getTodosFeatureState =
  createSelector(getFeatureState,state => state.todos);

const selectAllTodos = createSelector(getTodosFeatureState, selectAll);

【讨论】:

这不适用于 NGRX 实体。这是 entityState 接口 ``` export interface TodosState extends EntityState loading: boolean;加载:布尔值;错误:错误; ``` 你得到的错误是什么?

以上是关于无法读取未定义(读取“地图”)NGRX 实体的属性的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Ngrx 读取未定义的属性“firebaseApp”

使用地图时反应'无法读取未定义的属性'

错误:无法在 reactjs 项目中读取未定义的属性(读取“地图”)\

ReactJS TypeError:无法读取未定义的属性(读取“地图”)

错误:无法读取未定义的属性“地图”

TypeError:从 API 获取时无法读取未定义的属性(读取“地图”)