Ngrx : Cannot assign to read only property 'Property' of object '[Object]'

28,345

Solution 1

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] || [])]
  }

Solution 2

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

Solution 3

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));
Share:
28,345
infodev
Author by

infodev

Updated on July 05, 2022

Comments

  • infodev
    infodev almost 2 years

    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]'

  • CularBytes
    CularBytes over 3 years
    But the super annoying thing is that you want to modify for example a nested object (array in array in root object). As far as I can see, I have to do a deep-copy of the entire object tree and then update this in the store, which of course eats memory. How to avoid such a thing?
  • kvetis
    kvetis over 3 years
    Well the whole point of the redux pattern change detection is that you can check only the object references instead of deep comparing the objects. So in order for it to work you need to combine old references with the new references. If a deeply nested object is changed so is the tree above it and all the way to the root state. Then the change detection works. You can simplify the operation using a nested reducer.
  • Mansour Alnasser
    Mansour Alnasser about 3 years
    I'm impressed that no one +1 your answer, thanks, worked for me.
  • Daniel M Sánchez
    Daniel M Sánchez over 2 years
    You save my day, Thanks!!!
  • Ryan
    Ryan over 2 years
    You may not want to disable strictStateImmutability checks. This is there for a reason. When using immutable state properly, you can enable OnPush Change Detection. This will radically improve performance. Also you may lose the ability to use Time-Travel debugging. Removing this check removes a lot of the benefits of NgRx. Check out the NgRx Example App for examples.
  • griest
    griest about 2 years
    This didn't work for me in ngrx 13 in a testing environment.