Home > other >  NgRx select cannot read property of undefined
NgRx select cannot read property of undefined

Time:09-13

I am trying to retrieve the state for my application using NgRx but I cannot get it to work. I get the error:

ERROR TypeError: Cannot read properties of undefined (reading 'machines')

the error points to here in the selector below. I can add the ? operator, that causes the issue to go away but nothing render on the front end when i use the async pipe machine.selector.ts

export const selectMachines = (state: AppState) => state.machineState;
export const selectAllMachines = createSelector(
  selectMachines,
  (state) => state.machines
);

how I select the machines in my component

  public allDevices$ = this.store.select(selectAllMachines);

machine reducer and state

export interface AppState {
machineState: MachinesState;
}


export interface MachinesState {
machines: Machine[];
}

const initialState: MachinesState = {
machines: [
  { id: '1', name: 'WASHER #1', status: Status.Available, type: 'Washer' },
  {
    id: '2',
    name: 'WASHER #2',
    status: Status.UnAvailable,
    type: 'Washer',
    user: 'Flat 4',
  },
  { id: '3', name: 'DRYER #1', status: Status.Available, type: 'Dryer' },
  {
    id: '4',
    name: 'DRYER #2',
    status: Status.UnAvailable,
    type: 'Dryer',
    user: 'Flat 1',
  },
],
};

export const machineReducer = createReducer(
initialState,
on(updateMachine, (state, { machine }) => {
  const index = state.machines.findIndex((d) => d.id === machine.id);
  state.machines[index] = machine;
  return state;
}),
on(getMachines, (state) => {
  return state;
})
); 

CodePudding user response:

The way we set up our ngrx selectors is as follows:

  • Define a constant variable name for the reducer, ex: export const MY_CONSTANT = 'whatever'
  • In your selectors, create a feature selector as so: const getViewState = createFeatureSelector<IParticipantViewState>(MY_CONSTANT );
  • In your module define the following for the store, along with its reducer: StoreModule.forFeature(MY_CONSTANT , machineReducer , { initialState: myState}),

where myState is defined myState as initialState which is imported from your reducer file where the initial state is set up.

This way we wirte up the store module for our particular feature, with a default reducer and an initialState set up. The feature selector gives us the slice that we need.

Finally, in your createSelector it will look like what you made except you will pass in the getViewState variable as the first argument.

CodePudding user response:

This line

public allDevices$ = this.store.select(selectAllMachines);

should be in ngOnInit

something like

import * as machineSelector from '../location-of-your-machine-selector';

export class YourComponent implements OnInit {
   public allDevices$: Observable<Machine[]>;

   constructor(private store: Store<AppState>) {}

   ngOnInit(): void {
     this.allDevices$ = this.store.select(machineSelector.selectAllMachines);
   }
}

because its an async operation its a good idea to add an ngIf to the html to make sure the machines are loaded

<div  *ngIf="allDevices$ | async as machines">
  <div *ngFor="let machine of machines">{{machine.name}}</div>
</div>
  • Related