-1

I am trying to introduce ngrx into a large legacy angular app (upgraded to Angular 11). The app has a giant model, that holds pretty much all of the data and cannot be adjusted as it is widely used in the company.

The idea was to put an instance of the model into the ngrx store and update relevant parts with the help of "immer" and "ngrx-immer".

My problem is, that updating parts of it results in the following errors

// in the reducer    
dashboard.widgets.push(action.widget) 

ERROR TypeError: Cannot add property 1, object is not extensible
    at Array.push (<anonymous>)
    at dashboard.reducer.ts:39
    at ngrx-immer-shared.js:9
    at produce (immer.esm.js:1)
    at ngrx-immer-shared.js:9
    at ngrx-store.js:1265
    at combination (ngrx-store.js:242)
    at ngrx-store.js:747
    at ngrx-store.js:275
    at computeNextEntry (ngrx-store-devtools.js:426)

I also tried

immerOn(DashboardActions.addWidgetToDashboard, (state, action) => {
    const dashboard = state.dashboards.find(d => d.id === action.dashboardId)
    if (dashboard) {
      dashboard.widgets = [...dashboard.widgets, action.widget]
    }
  }),

ERROR TypeError: Cannot assign to read only property '_widgets' of object '[object Object]'
    at DashboardModel.set (dashboard.model.js:54)
    at DashboardModel.set (validation-properties.js:104)
    at dashboard.reducer.ts:39
    at ngrx-immer-shared.js:9
    at produce (immer.esm.js:1)
    at ngrx-immer-shared.js:9
    at ngrx-store.js:1265
    at ngrx-immer-shared.js:9
    at produce (immer.esm.js:1)
    at ngrx-immer-shared.js:9

The dashboard model has setters for its widget attribute and it is not readonly. According to https://immerjs.github.io/immer/update-patterns/#array-mutations this should work.

I know this structure isn't ideal but splitting the model up is not an option.

How can I update nested parts of my ngrx state? What am I doing wrong?

Edit:

The DashboardModel looks like this (removed irrelevant parts for simplicity)

export declare class DashboardModel extends SelectableVisuItemBaseModel {
    private _widgets;
    constructor(name?: string);
    get widgets(): WidgetBase[];
    set widgets(value: WidgetBase[]);
}

As you can see, it has an explicit setter for widgets. My editor thinks it is fine as well.

Taldorr
  • 175
  • 1
  • 4
  • 11
  • Ngrx uses inmutability, so instead of modifying you must reference to a new object/array/whatever. Maybe you find this helpful https://css-tricks.com/understanding-immutability-in-javascript/ – Osakr Jan 20 '22 at 12:15
  • 1
    @Osakr From my understanding, immer does just that. – Taldorr Jan 20 '22 at 12:17
  • but in the immerOn reducer ntohing is being returned. You must provide a new state so I guess after your condition you should return a copy of the state with the modified data – Osakr Jan 20 '22 at 12:28
  • 1
    @Osakr immerOn does not need to return a new state, as you make changes on the draft. I tried it with returning a state but the error is the same – Taldorr Jan 20 '22 at 13:45
  • Could you share a reproduction? This should "just" work – timdeschryver Jan 20 '22 at 13:51
  • Hi @timdeschryver thanks for that library and taking time to help me. I added a shortened version of the DashboardModel. Is it possible that ngrx or immer removes setters or makes it readonly at runtime to enforce immutability? – Taldorr Jan 21 '22 at 05:06
  • I think that setters are removed - to be sure I'll have to double check though – timdeschryver Jan 21 '22 at 07:29
  • @timdeschryver I found this: https://immerjs.github.io/immer/complex-objects/#semantics-in-detail, from my understanding it says that it should be no problem as long as getter and setter are present. I also just found out, that "state" and "state.dashboards" is console.logged as Proxy, however "state.dashboards[0]" is logged as DashboardModel when the editor said it should be a WriteableDraft – Taldorr Jan 21 '22 at 07:43

1 Answers1

0

I figured out, that disabling strictStateImmutability AND strictActionImmutability solves the problem.

The original problem might have come from annotations on the models setter but I am not sure.

Taldorr
  • 175
  • 1
  • 4
  • 11