NgRx/RxJS中Action传入Prop变为不可变的问题排查与解决
问题原因
NgRx 自9.0版本起,默认开启strictActionImmutability(严格Action不可变校验)运行时检查规则:所有被传入Action的props属性,无论后续是否经过Reducer处理、是否被存入Store,都会被NgRx做深度冻结处理,变为只读的不可变状态。
由于JavaScript中对象为引用传递,当你直接把本地持有的this.noteRequest实例作为props传入Action执行dispatch时,NgRx冻结Action属性的操作会直接作用在原对象引用上,导致本地持有的请求模型实例也被冻结,后续修改分页、搜索条件等属性时就会出现无法赋值的异常。这也是为什么你在dispatch时传入深克隆副本后功能恢复正常——克隆出的新对象被冻结不会影响你本地持有的原可变对象。
解决方案
你可以根据项目实际情况选择以下方案处理:
- 优先推荐:Dispatch时传入请求对象的副本,完全符合NgRx的不可变数据流设计。如果请求模型没有嵌套属性,使用浅克隆即可;存在多层嵌套属性时使用深克隆,避免原对象被冻结。
public getNotes(){ this.noteStore.dispatch( NotesActions.loadNotes({getNotesRequestModel: _.cloneDeep(this.noteRequest)}) ); } - 按需调整运行时校验配置(不推荐生产环境使用):如果你确认项目代码不会出现误改Action/State的逻辑,可以在根模块导入Store时关闭严格Action不可变校验,该操作会丢失NgRx提供的不可变异常告警,容易引发难以排查的数据流问题。
@NgModule({ declarations: [ AppComponent, ], imports: [ BrowserModule, StoreModule.forRoot(reducers, { metaReducers, runtimeChecks: { strictStateImmutability: true, strictActionImmutability: false // 关闭Action不可变严格校验 } }), // 其余业务模块导入 ], providers: [], bootstrap: [AppComponent] }) - 优化参数生成逻辑:不复用同一个可变对象存储请求参数,每次发起请求时基于当前的分页、搜索条件重新生成
GetNotesRequestModel实例,从根源上避免持有被冻结对象引用后修改的操作。
提示:NgRx默认开启Action不可变校验的核心目的是避免开发者在数据流链路外随意修改Action携带的状态,导致难以追踪的状态不一致问题,非特殊场景不建议关闭该校验规则。
内容的提问来源于stack exchange,提问作者adova
相关产品推荐
相关产品推荐

