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

升级NGRX 15.2后遇Cannot add property对象不可扩展错误

NgRx 15.2升级后修改Action属性报"object is not extensible"错误解决方法

问题背景

将NgRx相关依赖升级至15.2版本,具体版本如下:

  • @ngrx/component-store: "15.2"
  • @ngrx/effects: "15.2.0"
  • @ngrx/router-store: "15.2.0"
  • @ngrx/store: "15.2.0"
  • @ngrx/store-devtools: "15.2.0"

升级后,在Effects代码中尝试修改action.item的name和id属性时,触发错误:TypeError: Cannot add property name, object is not extensible

涉及的Effects代码:

users$= createEffect(() => this.actions$
.pipe(
  ofType(users.LOAD_USERS),
  tap((action: users.LoadUsersAction) => {
    action.item.name = action.payload;
    action.item.id = action.payload.map(item => item.id);
  })));

已尝试以下三种方法但均无效:

// 方法1
action.item = {
  ...action.item,
  name: action.payload,
  id: action.payload.map(item => item.id)
};
// 方法2
const newItem = {
  ...action.item,
  name: action.payload,
  id: action.payload.map(item => item.id)
};
// 方法3
action.item = Object.assign({}, action.item, {
  name: action.payload,
  id: action.payload.map(item => item.id)
});

原因分析

NgRx从15.x版本开始,所有Action对象默认被Object.freeze冻结,目的是严格遵循Redux架构的不可变性原则。这意味着:

  • 无法修改Action上的现有属性
  • 无法给Action添加新属性
  • 无法重新赋值Action的子属性(比如action.item)
    整个Action对象处于不可扩展、不可修改的状态。

解决方案

既然原Action不可修改,需根据实际需求选择以下合规方案:

方案1:创建新Action并分发

如果需要基于更新后的数据触发后续操作,生成新的Action并分发:

users$= createEffect(() => this.actions$
.pipe(
  ofType(users.LOAD_USERS),
  map((action: users.LoadUsersAction) => {
    const updatedItem = {
      ...action.item,
      name: action.payload,
      id: action.payload.map(item => item.id)
    };
    // 返回自定义的新Action,需提前在Action类型中定义
    return users.UPDATE_USER_ITEM({ item: updatedItem });
  })));

方案2:在Reducer中处理状态更新

如果目标是更新Store中的状态,不要在Effects中修改Action,而是在Reducer中基于原Action的payload计算新状态:

// 对应模块的Reducer
export function userReducer(state: UserState = initialState, action: UserActions): UserState {
  switch (action.type) {
    case users.LOAD_USERS:
      return {
        ...state,
        currentItem: {
          ...state.currentItem,
          name: action.payload,
          id: action.payload.map(item => item.id)
        }
      };
    default:
      return state;
  }
}

方案3:临时关闭Action冻结(不推荐)

若仅用于调试需要临时绕过限制,可在Store配置中关闭Action不可变性检查,但此操作违背架构原则,可能引发状态不一致问题:

// Store模块配置
StoreModule.forRoot(reducers, {
  runtimeChecks: {
    strictActionImmutability: false, // 关闭Action不可变性检查
    strictStateImmutability: true // 保持状态不可变性检查(推荐保留)
  }
})

关键提示

NgRx强制Action不可变性是为了保证状态变更可追溯、可预测,避免因意外修改Action导致的难以排查的状态问题。建议优先遵循架构原则,通过创建新Action或在Reducer中处理状态更新实现需求,不要直接修改原Action。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 21:06:30