在升级到ngrx 9之后,无法添加属性X,因为对象不可扩展。

16

我遇到了一个问题:

无法添加属性X,对象不可扩展

在将我的Angular项目更新为Angular 9,并更新了Ngrx版本后出现了这个问题。当我将Ngrx版本回滚到8时,它可以正常工作。但是我也需要将它升级到Angular 9更新的v9版本。这发生在我将其作为数据源添加到Material表中并添加了附加属性时。我认为这个附加属性的修改是导致这个问题的原因。但我从我们得到的内容创建了一个新的数组,并使用slice尝试了下面的操作:

 myDataArray.slice(0)

它也不起作用。

我参考了Ngrx版本8到9的更改列表和迁移指南,可以在这里找到https://ngrx.io/guide/migration/v9

我发现与Angular 9相关的一个特殊更改是关于不变性的。他们在那里定义了与行动、状态和序列化相关的不可变逻辑。我尝试了他们在Ngrx V9更新中建议的方法来解决这些问题,可以在这里找到https://ngrx.io/guide/store/configuration/runtime-checks

但这对我没有起作用。如果有人有解决该问题的解决方案,那将非常有帮助。谢谢。

错误堆栈跟踪..(我也使用了matDataFlatner,这就是对象突变发生的地方)

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)


2
你尝试过克隆来自 store 的对象吗?假设 myDataArray 通过选择器从 store 中获取,尝试执行 myDataArray = JSON.parse(JSON.stringify(myDataArray)) 或者其他深度克隆的方法。 - julianobrasil
如果您发布带有堆栈跟踪的实际错误信息,将会更有助于确定是哪个操作导致了此问题。 - Poul Kruijt
1
在完美的世界中,您应该尝试使用链接上与运行时检查相关的技术来解决问题。无论如何,如果您确实需要坚持深度克隆,可以尝试使用https://www.npmjs.com/package/fast-copy。 - julianobrasil
@julianobrasil 非常感谢您的支持。您可以将您的解决方案添加为答案,我可以批准它。 - PushpikaWan
这个回答解决了你的问题吗?无法添加属性“X”,对象不可扩展angular 9 - Alex Parloti
显示剩余3条评论
5个回答

28

由于myDataArray是从存储中通过选择器获取的,因此您应该进行深度克隆。保持存储数据的不可变性是redux模式的重要组成部分,如果修改myDataArray,您将直接更改存储中的数据(根据您的选择器,它可能是相同的数据 => 存储中数组的引用)。

在尝试对其进行任何更改之前,您可以使用myDataArray = JSON.parse(JSON.stringify(myDataArray))

有更有效的方法来深度克隆对象,例如使用fast-copy:

import copy from 'fast-copy';

...

myDataArray = copy(myDataArray);

6
你可以使用lodash库中的cloneDeep函数对对象进行深拷贝,避免出现错误:

import {cloneDeep} from 'lodash';

const clonedData = cloneDeep(myDataArray);

然后,你可以向clonedData对象添加属性或其他你想要的内容。

祝好!


5
我参考了Ngrx 8到9版本的变更列表和迁移指南。

https://ngrx.io/guide/migration/v9

我发现在Angular 9中有一个特殊的与不可变性相关的变化。他们定义了与动作、状态和可序列化性相关的不可变逻辑。我尝试了他们建议的方法来解决Ngrx V9更新中的这些问题。

https://ngrx.io/guide/store/configuration/runtime-checks

你可以进行以下更改:


@NgModule({
  imports: [
  StoreModule.forRoot(reducers, {
    runtimeChecks: {
      strictStateImmutability: false,
      strictActionImmutability: false,
    },
  }),
 ],
})
export class AppModule {}

@ngrx/store ships with five (5) built-in runtime checks. Try disabled all checks

谢谢!在我将旧项目中的 ngrx(从 6 更新到 9)后,这解决了我的问题,但随之而来的是 MomentJS 解析字符串的能力被破坏:“Cannot add property _weekdaysShortRegex, object is not extensible”。可以肯定的是,在此项目中,MomentJS 将被优先替换。 - user3819197

0
为了解决这个问题,我们需要克隆选择器(createSelector)的数据。
例如:在 Ngrx 或 Angular 升级之前,您的代码如下:
export const getFrameworkCitationsSuccess = createSelector(getFrameworkState, (state: FrameworkState) => {
  return { frameworkCitations: state.frameworkCitations, filters: state.filters };
});

在升级 Ngrx 或 Angular 后,请进行以下更改:

export const getFrameworkCitationsSuccess = createSelector(getFrameworkState, (state: FrameworkState) => {
  return { frameworkCitations: [...state.frameworkCitations], filters: {...state.filters} };
});

SS: 在此输入图像描述


0

由于需要保持存储的数据不可变,因此您需要进行深度克隆(正如其他答案所提到的)。

另一种深度克隆对象的方法是使用structuredClone方法。

例如:

newLoadList = structuredClone(state.loadsList);

现在,newLoadList是状态属性loadsList的深层副本。

因此,在reducer中,您可以使用newLoadsList来添加、更新等操作,然后将其返回到状态中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接