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

为什么NgRx不直接修改store状态?reducer返回新状态有什么规范?

为什么不能直接修改Store状态,必须通过Reducer返回新副本

  • 保证状态变化可追溯、可调试:所有状态修改都必须对应明确的Action,集中在Reducer中处理,你可以通过NgRx DevTools完整回溯每一次状态变化的触发原因、变化前后的值,甚至可以回放操作流程。如果允许直接修改原状态,状态变化会散落在应用各个角落,出问题时根本没法定位是哪里触发了异常修改。
  • 保证响应式更新正常工作:NgRx的状态订阅、selector选择器都是通过引用对比判断状态是否发生变化的,如果你直接修改原状态,对象引用不会变化,NgRx就会认为状态没有更新,依赖该状态的组件、effect都不会触发更新,出现视图不同步的问题。返回新副本会生成新的引用,就能正确触发所有依赖该状态的更新逻辑。
  • 避免副作用与竞态问题:Reducer是纯函数,相同的state和action输入永远会得到相同的输出,没有额外副作用。所有状态更新的顺序完全由Action派发顺序决定,不会出现多个地方同时修改状态导致的不可预期的竞态问题,保证了状态的一致性。

返回新状态的规则

有通用的核心规范必须遵守,具体写法可以根据状态结构灵活调整:

必须遵守的通用规范

  1. 永远不直接修改传入的原state对象,不管是顶层state还是嵌套的子属性,都禁止直接赋值、调用会修改原对象/数组的方法(比如数组的push/splice/pop、对象直接改属性值)
  2. 只需要对发生修改的属性路径上的所有层级做浅复制,未修改的属性直接复用原有引用即可,不需要做全量深复制(会浪费性能)
  3. 修改完成后必须返回完整的新状态对象,不能只返回修改的部分属性

不同结构的灵活调整方式

  • 像你示例中修改顶层数组属性的场景,只需要复制顶层state,再复制要修改的ingredients数组追加新值即可,和你写的示例代码完全一致
  • 如果是修改深层嵌套的属性,就需要逐层复制到修改的属性层级,比如修改state下user对象的address属性的city字段,写法如下:
return {
  ...state, // 复制顶层state
  user: {
    ...state.user, // 复制user对象
    address: {
      ...state.user.address, // 复制address对象
      city: action.payload.newCity // 仅修改目标属性
    }
  }
}
  • 如果状态嵌套层级过深,逐层写展开运算符过于繁琐,也可以用Immer库简化不可变更新的写法,直接写修改原对象的语法,Immer底层会自动帮你生成新的状态副本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 19:42:02