@ngrx/entity `Cannot read property 'ids' of undefined` when using together with @ngrx/store? already accounting for custom ids when defining adapter

Viewed 6282

I have looked at the 2 other posts regarding this, the one with the wrapping around the larger state does not apply whilst the other one with registering multiple forFeature -- I have done some testing around that and even if I import it before or after the forRoot(reducers) in my app.module, it makes no difference.

I'm sure I'm missing something obvious around the configuration.

My configuration:

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);

My actual reducer:


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

I register in my Order.Module as:

    StoreModule.forFeature('orders', ordersReducer),

My main reducer map:

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

Then the main import in app.module:

    StoreModule.forRoot(reducers),

Trying to read the entities:

Constructor:

 private store: Store<AppState>

Actual function:

this.store
      .select(ordersReducer.selectAll)

Where that import is taken from:

import * as ordersReducer from '../../../../../store/reducers/orders.reducer';
4 Answers

I had this error because I was setting up my selector with the wrong feature key:

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

it should match what you have loaded in the feature module:

StoreModule.forFeature('feature_key', iocReducer),

I had a similar error to that of yours in entity.js (NGRX implementation file). In my case, I had a lazy-loaded module with its own state that was initially undefined. Since entity selectors don't do any null check prior to grabbing the entity related fields (e.g. ids) it causes this error. My solution was to avoid using orderAdapter.getSelectors(OrdersState) and instead define my own protected selectors which are fairly simple:

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);

I don't love the fact that I'm not using the built-in selectors but at least this doesn't break my code. I hope the NGRX team provides a way to avoid situations like this.

It looks like it's trying to read from OrdersState before it has been initialized. Your reducer doesn't actually return the default state in order to initialize it; At least you don't seem to, at the code you present.

Make your reducer return the default state.

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
        }
    }
}

I had the same error, when I forgot to add the "state" as as a second parameter for adapter.addOne(). So in OPs example if the redcuer was written in following way:

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

then the error would be exactly the same. In my case this was:

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)
Related