我们有下面的ngrx减速器,但是在我们的状态中添加了一个嵌套对象(structure: settings: { a: boolean, b: string }
),所以决定使用lodash的 cloneDeep(obj)
而非 ...obj
传播操作符,以确保我们的状态是不可改变的,根据官方文档中的第二条说明。"创建还原器功能" 节。
展开运算符只做浅层复制,不处理深层嵌套的对象。你需要复制对象中的每一个层次,以确保不变性。有一些库可以处理深度复制,包括 宿雾 和 沉浸.
// before:
export function reducer(state: MyState = initialState, action: Actions): MyState {
switch (action.type) {
// ...
case MY_TASK: {
return {
...state,
prop1: action.payload.prop,
prop2: initialState.anotherProp
};
}
// ..
}
}
当我们把它改成下面的样子时,ngrx一直在调用 reducer()
方法,有效地阻止(崩溃)了浏览器。
// after - causes infinite loop
import * as _ from 'lodash';
// ..
export function reducer(oldState: MyState = initialState, action: Actions): MyState {
function nextState(stateChanges?: (MyState) => void): MyState {
const draftState: MyState = _.cloneDeep(oldState);
if (stateChanges) {
stateChanges(draftState);
}
return draftState;
}
switch (action.type) {
// ...
case MY_TASK: {
return nextState(s => {
s.prop1 = action.payload.prop;
s.prop2 = initialState.anotherProp;
});
}
// ..
}
}
stateChange
甚至不接触新的嵌套的 附加状态 (即 settings
对象),只有平面的(prop1, prop2
)我们之前的。settings
然而,是属于 oldState
于是就传到了罗达什的 cloneDeep()
.
知道为什么要改变返回状态吗? (即使用 _.deepClone(state)
而不是铺开 ...state
操作者) 会有这样的效果--如何阻止它?
我们正在使用 @ngrx/entity
,所以状态在延伸 EntityState<MyState>
. 我不明白 库的实体functinos返回一个新的状态。 (或 "如果不做任何改变,则为同样的状态"),并且, 附加属性 像我们新的嵌套式 settings
对象可以添加到状态中,但 "这些属性必须手动更新"--我认为这意味着我必须自己克隆它们。
是否我们试图克隆整个状态(它扩展了 EntityState<T>
),而不仅仅是 附加属性?
工作代码和坏掉的代码之间的唯一变化是在 reducer()
方法,所以我不认为我们不小心触发了任何我们之前没有触发的新事件,这通常会导致一个无限循环......。
_.cloneDeep
是最坏的情况 ngrx
因为这意味着所有的东西都已经在商店里被触动过了,它再次触发了所有的选择器。
从描述的错误中,我可以假设你有一个 selector
导致 dispatch
在某些情况下,由于被派遣的 action
造成 _.cloneDeep
选择器再次被触发,导致 dispatch
等...