You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular只读属性报错Cannot assign to read only property

问题根因

这个只读报错是NgRx开发模式默认开启的严格不可变校验导致的,触发逻辑非常明确:

  • NgRx为了保证状态可预测、不被意外修改,在开发模式下会对所有传入Action的payload、所有存储在State中的对象做递归冻结(Object.freeze+Proxy拦截),任何直接修改这些冻结对象的操作都会抛出你看到的类型错误。
  • 你第一次调用dispatch方法时,直接把组件自身持有的this.pageable实例作为query参数传给了Action,NgRx直接把这个原对象的引用冻结了——这时候哪怕请求还没返回,你修改这个pageable的属性已经会报错。
  • 后续请求完成后,你从Store拿到的response是State中存储的冻结对象,虽然用了展开运算符做浅拷贝,但在高版本NgRx的严格校验下,浅拷贝没有完全脱离Store的只读代理,拿到的对象仍然处于被拦截修改的状态。
解决方案

按优先级选择即可:

  • 方案1(推荐):永远不要把组件里的可变对象直接作为Action payload传入Store,传之前做深拷贝切断引用关联,避免NgRx冻结组件上的原对象
    修改findList方法:
    findList(request: Pageable){
      // 深拷贝后再传入Store,原组件对象不会被冻结
      const query = JSON.parse(JSON.stringify(request)) as Pageable;
      this.categoryStore.dispatch(findListAction({query}));
    }
    
  • 方案2(推荐):从Store取数据赋值给组件可变属性时,重新创建自定义类的实例,彻底脱离Store的代理和冻结逻辑,不要依赖浅拷贝
    修改Store订阅逻辑:
    this.categoryStore.pipe(select(listSelector)).subscribe((response: FindResponseModel | null) => {
      this.categories = [];
      if (!response) return;
      response.items.forEach((item: CategoryModel) => {
        this.categories.push(item);
      });
      // 重新创建Pageable实例,逐个赋值属性,完全切断和Store冻结对象的引用
      const req = response.request;
      this.pageable = new Pageable(req.key);
      this.pageable.page = req.page;
      this.pageable.quantity = req.quantity;
      this.pageable.order = req.order;
      this.pageable.search = req.search;
    });
    
  • 方案3(仅临时调试用):如果需要快速本地验证,可以临时关闭NgRx的严格不可变检查,在根模块导入Store时添加配置:
    StoreModule.forRoot(reducers, {
      runtimeChecks: {
        strictStateImmutability: false,
        strictActionImmutability: false,
      }
    })
    

    注意:该配置会关闭NgRx核心的不可变校验,开发时很容易出现直接修改状态导致的难排查bug,禁止在生产环境使用。

踩坑提醒:用展开运算符{...obj}拷贝Store返回的对象是NgRx新手最常踩的坑,展开运算符仅复制对象第一层的基本类型属性,如果对象被Proxy做了只读代理、或是存在嵌套冻结属性,浅拷贝无法生成完全可写的新对象。最稳妥的处理原则是:Store里流出的所有状态都是只读的,组件要修改的数据必须自己持有独立实例,不要直接改动Store返回的对象引用。

内容的提问来源于stack exchange,提问作者Faabass

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 11:15:54