为何组件内修改数组会直接更新NgRx Store?
priorities数组会直接更新NgRx Store? 嘿,这个问题的核心其实是JavaScript引用类型的特性,结合NgRx的状态管理逻辑就能轻松搞清楚啦:
原因分析
引用类型的内存共享特性
在JavaScript里,数组属于引用类型。当你在subscribe回调里执行this.priorities = data.prioritySettings时,并没有把Store里的数组完整复制一份给组件,只是把指向原数组的内存引用赋值给了this.priorities。这意味着:组件里的priorities和NgRx Store中projectState.prioritySettings指向的是同一个内存地址的数组。所以你在组件里修改priorities(比如用push、splice或者直接修改数组元素),本质上就是在修改Store里的原数组。NgRx的不可变状态原则被绕过
NgRx的设计核心要求状态是**不可变(Immutable)**的,所有状态更新必须通过触发Action、由Reducer纯函数来完成。但直接修改引用类型的状态,会完全绕过这个机制,导致Store状态被意外篡改,还可能引发难以追踪的bug(比如订阅该状态的其他组件无法正确响应变化)。
解决方法
要切断组件内变量和Store状态的引用关联,你只需要在赋值时创建一个新的数组实例即可,常用的方式有:
1. 使用扩展运算符创建新数组
this.prioritySettingSub = this.store.select('projectState').subscribe(data => { // 通过扩展运算符生成新数组,彻底切断引用 this.priorities = [...data.prioritySettings]; })
2. 使用Array.from()方法
this.priorities = Array.from(data.prioritySettings);
3. 处理对象数组的深引用场景
如果prioritySettings是对象数组,上面的方法只能做浅拷贝,此时可以用JSON.parse(JSON.stringify())(适合结构简单的对象)或者第三方库(如Immer、Lodash的cloneDeep)来做深拷贝:
// 简单场景下的深拷贝方案 this.priorities = JSON.parse(JSON.stringify(data.prioritySettings));
另外,更规范的NgRx实践是在Selector中就返回不可变的结果,或者使用NgRx的实体适配器(Entity Adapter)来管理集合状态,从根源上避免引用共享的问题。
内容的提问来源于stack exchange,提问作者Tahum

