13
votes

I'm using ngrx store.

In my state I have to items

export interface ISchedulesState {
  schedulings: ISchedules;
  actualTrips: ISchedule[];
}

Here are my interfaces

export interface ISchedules {
  [key: string]: ISchedule[];
}

export interface ISchedule {
  dest: number;
  data: string
}

In reducer I update actualTrips

export const SchedulingReducers = (
  state = initialSchedulingState,
  action: SchedulesAction
): ISchedulesState => {
  switch (action.type) {
    case ESchedulesActions.GetSchedulesByDate: {
      return {
        ...state
      };
    }
    case ESchedulesActions.GetSchedulesByDateSuccess: {
      return {
        ...state,
        schedulings: action.payload
      };
    }
    case ESchedulesActions.GetSchedulesByTime: {
      let time = action.payload;
      state.actualTrips = [...(state.schedulings[time] || [])]; // if not data return empty array
      return state;
    }
    default:
      return state;
  }
};

But actually I get an error

ERROR TypeError: Cannot assign to read only property 'actualTrips' of object '[object Object]'

3

3 Answers

15
votes

The basic principle of Redux pattern is immutability of state and its parts, because it let's us to detect changes just by object reference instead of comparing whole objects.

In your reducer, you cannot directly assign a property of state (state.actualTrips =), because change detector (and selectors) would not detect it as changed.

To modify state, you return a copy of the state with new modifications.

  const time = action.payload;
  return {
      ...state,
      actualTrips: [...(state.schedulings[time] || [])]
  }
4
votes

If you want to change state.actualTrips = myNewValue is not allowed because there is a strict Setting. So one way is may to clonedeep and return the object, like newState = cloneOfState... I didn't test it. So I changed the setting in app.module for Store. My Example: change the strictStateImmutability to false (full Docu here: https://ngrx.io/guide/store/configuration/runtime-checks )

    StoreModule.forRoot(ROOT_REDUCERS_TOKEN, {
        metaReducers,
        runtimeChecks: {
            // strictStateImmutability and strictActionImmutability are enabled by default
            strictStateSerializability: true,
            strictActionSerializability: true,
            strictActionWithinNgZone: true,
            strictActionTypeUniqueness: true,
            // if you want to change complexe objects and that we have. We need to disable these settings
            // change strictStateImmutability, strictActionImmutability
            strictStateImmutability: false, // set this to false
            strictActionImmutability: true,
        },
    }),
2
votes

That error happened me when I changed the input values in the template. I was using Angular11 + NGRX11 so I understood I was changed a value from store, this was my fix:

Before:

 this.store.dispatch(new Actions.LoginUser({ user: this.user }));

After:

 const clone = { 
  user: Object.assign({}, this.user) 
 };
 this.store.dispatch(new Actions.LoginUser(clone));