在ngrx中创建reducer函数时,我读过的所有内容都说我应该返回原始/先前状态的副本。通过使用传播运算符或通过使用库或诸如JSON.parse(JSON.stringify(state))
之类的技巧。
但是我发现那里有一个陷阱,找不到任何谈论它的人。 在reducer中返回的最后一个状态是将与所有当前订户和将来的订户共享的状态。 这意味着所有使用特定存储的组件都将看到相同的状态对象。
这也意味着,如果在一个组件中更改状态中的任何值(不调度动作),则存储实际上将修改该值,但不会通知其他组件。 如果要在任何地方共享当前状态的副本,那有什么意义呢?
不可变这个词一直被使用,但是这个状态根本不是不可变的,因为存储返回了它自己的内部对象,而不是它的副本。
我了解不变部分是否是开发人员必须遵循的概念。但是,然后需要在使用原始对象/值的组件中进行复制。从reducer返回浅拷贝或深拷贝似乎只是在浪费处理能力和内存。
答案 0 :(得分:2)
我会尽力回答。
伪代码中的reducer看起来像这样:
myReducer(state, action) {
switch(action) {
case ACTION_1:
return {...state, prop: action.payload}
case ACTION_2:
const newState = _.cloneDeep(state)
newState.prop = action.payload
return newState
default:
return state
}
}
如果是ACTION_1,则您不是突变状态。传播算子使用新参考创建一个新对象,而新参考是发出更改信号所需要的。
如果是ACTION_2,则您正在克隆状态。您突变克隆的状态并返回它。因为克隆状态有一个新的对象引用,所以它表示已进行更改,并且每个人都很高兴。
在默认情况下,任何其他操作(例如ACTION_3)都将被忽略,并返回原始状态,表示该状态未更改。对象引用未更改,因此发出“无更改”的信号(这就是为什么重要的是不要更改原始状态)。
当一个动作被触发时,该动作被传递给每个reduce。因此,不想修改其关联状态的化简器可以依靠默认的case语句忽略该动作。
一个动作可以并且经常会触发多个减速器的状态变化。
如果返回的对象引用已更改,它将为所讨论的特定状态触发任何相关的RxJS状态订阅。可以使用一些不错的ngrx选择器来最小化触发哪些订阅。
PS有一个很棒的库,名为ngrx-store-freeze,它将强制执行“无突变”原则。如果您更改状态,它将及早引发错误。这有助于避免难以跟踪的错误。使用meta reducer冻结商店冻结。
PPS使用对象引用确定更改的全部目的是因为检查对象引用比检查对象上的每个值以查看其是否已更改要快得多。这就是不变性如此重要的原因。