0

0

NGRX 中 Action 不可变性导致的 TypeError 解决方案

花韻仙語

花韻仙語

发布时间:2026-03-18 17:39:15

|

572人浏览过

|

来源于php中文网

原创

NGRX 15+ 默认启用严格不可变性检查,直接修改 action 对象属性(如 action.item.name)会触发 “Cannot add property, object is not extensible” 错误;根本解决方式是遵循不可变原则创建新对象,而非就地修改。

ngrx 15+ 默认启用严格不可变性检查,直接修改 action 对象属性(如 `action.item.name`)会触发 “cannot add property, object is not extensible” 错误;根本解决方式是遵循不可变原则创建新对象,而非就地修改。

在 NGRX 15.x 及更高版本中,@ngrx/store 默认启用了运行时不可变性检查(runtime checks),其中 strictStateImmutability 和 strictActionImmutability 均为 true。这意味着:所有 action 实例(包括其嵌套属性)均被 Object.freeze() 处理,变为不可扩展、不可修改的对象。因此,以下写法必然失败:

tap((action: users.LoadUsersAction) => {
  action.item.name = action.payload; // ❌ TypeError: Cannot add property name, object is not extensible
  action.item.id = action.payload.map(item => item.id); // ❌ 同样报错
});

即使尝试浅拷贝(如 {...action.item} 或 Object.assign),若 action.item 本身也被冻结(NGRX 默认递归冻结嵌套对象),仍无法赋值。

✅ 正确做法:始终返回新对象,不修改原 action

应将逻辑重构为纯函数式操作,在管道中使用 map 或 switchMap 等操作符生成全新 action 负载或副作用数据,而非篡改输入 action:

users$ = createEffect(() => 
  this.actions$.pipe(
    ofType(users.LOAD_USERS),
    map((action: users.LoadUsersAction) => {
      // ✅ 安全:基于原 action 构建全新数据结构,不修改任何冻结对象
      const newItem = {
        ...action.item,
        name: action.payload, // 假设 payload 是 string
        id: Array.isArray(action.payload) 
          ? action.payload.map((item: any) => item.id) 
          : [action.payload?.id] // 根据实际类型调整
      };
      return { newItem }; // 或 dispatch 新 action,如 users.UPDATE_ITEM({ item: newItem })
    }),
    tap(({ newItem }) => {
      console.log('Processed item:', newItem);
      // 执行副作用(如日志、埋点),但不修改 state 或 action
    })
  )
);

⚠️ 注意:tap 仅用于副作用(无返回值),不可用于转换数据流;数据转换必须使用 map、switchMap 等返回新 observable 的操作符。

Hotpot AI Background Remover
Hotpot AI Background Remover

Hotpot.ai推出的图片背景移除工具

下载

? (不推荐)临时禁用不可变性检查(仅限调试)

若需快速验证逻辑(例如迁移旧代码阶段),可通过 Store 配置临时关闭检查(生产环境严禁使用):

// app.config.ts 或 StoreModule.forRoot() 配置中
StoreModule.forRoot(reducers, {
  runtimeChecks: {
    strictStateImmutability: false,
    strictActionImmutability: false,
    // 其他检查项可视情况关闭
  }
}),

但此举会掩盖潜在 bug,削弱 NGRX 的核心优势——可预测的状态演进与时间旅行调试能力。

✅ 最佳实践总结

  • 永远不要修改 action 或 state 的任何字段,包括深层属性;
  • 使用展开运算符({...obj})、Object.assign({}, obj, updates) 或专用库(如 immer 配合 produce)安全生成新对象;
  • 在 Effect 中,将数据处理逻辑放在 map/switchMap 内,确保输出为不可变新值;
  • 利用 TypeScript 类型系统提前约束 action 结构,避免运行时意外属性赋值;
  • 将副作用(如 API 调用、路由跳转)置于 tap,将状态变更逻辑交由 reducer 处理。

遵循不可变性原则不仅是规避错误的手段,更是构建可维护、可测试、可回溯的响应式应用的基石。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

565

2023.06.20

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

443

2023.07.04

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

803

2023.07.04

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

494

2023.09.01

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

678

2023.09.04

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

1140

2023.09.04

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

674

2023.09.12

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

562

2023.09.20

Python WebSocket实时通信与异步服务开发实践
Python WebSocket实时通信与异步服务开发实践

本专题聚焦 Python 在实时通信场景中的开发实践,系统讲解 WebSocket 协议原理、长连接管理、消息推送机制以及异步服务架构设计。内容包括客户端与服务端通信实现、连接稳定性优化、消息队列集成及高并发处理策略。通过完整案例,帮助开发者构建高效稳定的实时通信系统,适用于聊天应用、实时数据推送等场景。

7

2026.03.18

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号