@ngrx/entity `与@ngrx/store一起使用时无法读取未定义的属性'ids'?定义适配器时已经考虑了自定义 ID

问题描述 投票:0回答:5

我已经查看了与此相关的另外 2 篇文章,其中一个包含较大状态的文章不适用,而另一篇则注册了多个 forFeature - 我已经对此进行了一些测试,即使我在之前或之后导入它我的 app.module 中的 forRoot(reducers) 没有什么区别。

我确信我在配置中遗漏了一些明显的东西。

我的配置:

export const orderAdapter: EntityAdapter<IOrder> = createEntityAdapter<IOrder>({
  selectId: (order: IOrder) => order._id
});

export interface AllOrdersState extends EntityState<IOrder> {}

export const initialState: AllOrdersState = orderAdapter.getInitialState({
  ids: [],
  entities: {}
});

export const OrdersState = createFeatureSelector<AllOrdersState>('orders');

export const {
  selectIds,
  selectEntities,
  selectAll,
  selectTotal
} = orderAdapter.getSelectors(OrdersState);

我的实际减速器:


export function ordersReducer(
  state: AllOrdersState = initialState,
  action: actions.OrdersActions
) {
    case actions.CREATE_MANY: {
      console.log(action.orders);
      return orderAdapter.addMany(action.orders, state);
    }
}

我在我的 Order.Module 中注册为:

    StoreModule.forFeature('orders', ordersReducer),

我的主减速器图:

const reducers: ActionReducerMap<AppState> = {
  order: orderReducer,
  admin: adminReducer,
  tlabs: tlabsReducer,
  reports: reportsReducer,
  settings: settingsReducer,
  orders: ordersReducer
};

然后是app.module中的主要导入:

    StoreModule.forRoot(reducers),

尝试读取实体:

构造者:

 private store: Store<AppState>

实际功能:

this.store
      .select(ordersReducer.selectAll)

导入来源:

import * as ordersReducer from '../../../../../store/reducers/orders.reducer';
angular ngrx angular8 ngrx-store ngrx-entity
5个回答
5
投票

我遇到此错误是因为我使用错误的功能键设置选择器:

export const selectIocState = createFeatureSelector<fromIoc.IocState>('this_key_was_wrong');

它应该与您在功能模块中加载的内容相匹配:

StoreModule.forFeature('feature_key', iocReducer),

4
投票

我在entity.js(NGRX实现文件)中遇到了与您类似的错误。就我而言,我有一个延迟加载的模块,其自己的状态最初是未定义的。由于实体选择器在获取实体相关字段(例如 ids)之前不执行任何 null 检查,因此会导致此错误。我的解决方案是避免使用

orderAdapter.getSelectors(OrdersState)
,而是定义我自己的受保护选择器,这非常简单:

export const selectIds = createSelector(selectOrderState, (state) => state?.ids);
export const selectEntities = createSelector(selectOrderState, (state) => state?.entities);
export const selectAll = createSelector(selectOrderState, (state) => (state?.ids as Array<string|number>)?.map(id => state?.entities[id]));
export const selectTotal = createSelector(selectOrderState, (state) => state?.ids?.length);

我不喜欢我没有使用内置选择器,但至少这不会破坏我的代码。我希望 NGRX 团队提供一种方法来避免此类情况。


2
投票

看起来它正在尝试在初始化之前从

OrdersState
读取。您的减速器实际上并不返回默认状态以对其进行初始化;而是返回默认状态。至少在你提供的代码中你似乎没有。

让你的reducer返回默认状态。

export function ordersReducer(
  state: AllOrdersState = initialState,
  action: actions.OrdersActions
) {
    switch(action.type) {
        case actions.CREATE_MANY: {
          console.log(action.orders);
          return orderAdapter.addMany(action.orders, state);
        }

        default: { 
          return state // Here you should return the default state
        }
    }
}

0
投票

当我忘记添加“state”作为adapter.addOne()的第二个参数时,我遇到了同样的错误。因此,在 OP 示例中,如果 redcuer 是按以下方式编写的:

      return orderAdapter.addMany(action.orders); // !! lack of "state" as 2nd param

那么错误将完全相同。就我而言,这是:

Uncaught TypeError: Cannot read properties of undefined (reading 'ids')
    at Object.operation [as addOne] (entity.js?9b56:59:1)
    at eval (eval at <anonymous> (mapping-fields-group.reducer.ts?fd4f:1:1), <anonymous>:1:17)
    at eval (mapping-fields-group.reducer.ts?fd4f:13:1)
    at eval (store.js?d450:1204:1)
    at combination (store.js?d450:215:1)
    at mappingsDataReducers (mappings-data-state.ts?7c86:21:1)
    at eval (store.js?d450:265:1)
    at combination (store.js?d450:215:1)
    at eval (store.js?d450:254:1)
    at computeNextEntry (store-devtools.js?777a:445:1)

0
投票

对于那些在运行时遇到这个问题的人

ng test
,我收到此错误,因为我需要设置一个模拟商店和模拟选择器进行测试。

有关如何设置测试模拟的详细信息,请参阅NgRx 测试 > 使用模拟选择器文档。

这是我在测试中如何实现这一点的示例:

describe('DataManagerService', () => {
  let service: DataManagerService;
  let store: MockStore;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        provideMockStore({
          selectors: [{
            selector: myEntityFeature.selectAll,
            value: [{id: 1, name: 'Mock Entity'}],
          };],
        }),
      ],
    });
    service = TestBed.inject(DataManagerService);
    store = TestBed.inject(MockStore);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });
});

注意:我使用 NgRx 原理图生成实体、操作、减速器和效果作为特征。因此,在我定义选择器名称

myEntityFeature.selectAll
的行中,您需要将其替换为对实际选择器的引用。同样,
value
应替换为与选择器中定义的接口匹配的模拟对象。

© www.soinside.com 2019 - 2024. All rights reserved.