Search code examples
angularreduxngrxangular9

Cannot add property X, object is not extensible after ngrx 9 update


I am having an issue like

Cannot add property X, object is not extensible

after updating my angular project to angular 9 with Ngrx update as well. When I rollback Ngrxversion to 8 it's working fine. But I need to update that as well to v9 with angular 9 updates. This has happened when I add this as datasource.data in the material table with additional attribute. I think that additional attribute alteration is a reason for that. But I create new array from what we got and tried out like below by using slice.

 myDataArray.slice(0)

It also not working.

I refer change list of Ngrx version 8 to 9 and migration guideline here https://ngrx.io/guide/migration/v9

As I found there is a special change related to immutability with angular 9. They have defined Action, state and serializability related immutability logic there. And I tried out the method that they have suggested to resolve those issues with Ngrx V9 update here https://ngrx.io/guide/store/configuration/runtime-checks

But those are not worked for me. It's really helpful if anyone has a solution to this issue. Thanks in advance..

error stack trace.. (I used matDataFlatner as well that's where the object mutation happens)

app-error-handler.ts:30 TypeError: Cannot add property level, object is not extensible at MatTreeFlattener.defaultFlattenerTransform [as transformFunction] (tree-table-flattener-builder.ts:57) at MatTreeFlattener._flattenNode (flat-data-source.ts:58) at flat-data-source.ts:81 at Array.forEach () at MatTreeFlattener._flattenChildren (flat-data-source.ts:78) at MatTreeFlattener._flattenNode (flat-data-source.ts:65) at flat-data-source.ts:92 at Array.forEach () at MatTreeFlattener.flattenNodes (flat-data-source.ts:92) at MatTreeFlatDataSource.set (flat-data-source.ts:138)


Solution

  • You should deep-clone myDataArray because it's coming out from the store through a selector. Keeping the immutability of the data in the store is an important part of redux pattern and you'd be changing the data directly in the store if you modify myDataArray (depending on your selector, it could be the same data => a reference to the array in the store).

    You can do myDataArray = JSON.parse(JSON.stringify(myDataArray)) before trying to make any change in it.

    There are more efficient ways of deep-cloning an object, for example using fast-copy:

    import copy from 'fast-copy';
    
    ...
    
    myDataArray = copy(myDataArray);