我正在寻找一种解决方案,以使选择器仅在与上次发出的值相比发生更改时才发出新值,并且不仅更改对存储的引用。
我的商店中有以下状态:
{
items: [],
loading: false,
selectedItemId: 1
}
我有以下选择器:
export const getSelectedItem = createSelector(getItemsState,
(state) => {
return state.selectedItemId === null ? null : state.items.find(item => item.id === state.selectedItemId)
}
);
当我对此选择器进行订阅时,每次例如在商店中的加载标志更改时,都会触发该事件。我希望选择器仅在所选项目的值更改时发出一个值。即从1到2。但是当状态对象的引用不同时,则不会。
我为此找到了一个解决方案:
this.itemStore.select(getSelectedItem).distinctUntilChanged((x, y) => {
return x.id === y.id;
}).subscribe(item => {
// do something
});
但是,我想移动逻辑以使更新的内容与我的选择器区分开,而不是将其放在调用方。
我了解为什么会有这种行为,因为框架每次都检查简化时是否检查对象是否相等,并且商店引用发生了变化,因为我们每次从化简器返回一个新的ItemState对象时,都会发生这种情况。但是我无法找到解决问题的方法,而且我无法想象我是唯一需要选择器的人,选择器仅在有效值已更改时才会更新。
reducer的代码如下:
export function itemsReducer(state: ItemsState = initialItemsState, action: Action): ItemsState {
switch(action.type) {
case itemActions.ITEMS_LOAD:
return {
...state,
itemsLoading: true
};
case itemActions.ITEMS_LOAD_SUCESS:
return {
...state,
items: action.payload,
itemsLoading: false
};
// ... a lot of other similar actions
}
}
答案 0 :(得分:3)
对于您当前的减速器,distinctUntilChanged
是处理此问题的正确运算符。解决此问题的另一种方法是,使您的reducer在更新项目时检测对象是否“功能上不变”,例如:
export function itemsReducer(state: ItemsState = initialItemsState, action: Action): ItemsState {
switch(action.type) {
case itemActions.ITEMS_LOAD:
return {
...state,
itemsLoading: true
};
case itemActions.ITEMS_LOAD_SUCESS:
return {
...state,
items: action.payload.map((newItem) => {
let oldItem = state.items.find((i) => i.id == newItem.id);
// If the item exists in the old state, and is deeply-equal
// to the new item, return the old item so things watching it
// know they won't need to update.
if (oldItem && itemEqual(newItem, oldItem)) return oldItem;
// Otherwise, return the newItem as either there was no old
// item, or the newItem is materially different to the old.
return newItem;
}),
itemsLoading: false
};
// ... a lot of other similar actions
}
}
您将需要以某种方式实现itemEqual来检查深度逻辑相等性。在这里,您可以使用功能上“相等”的含义来确定项目是否已更改,例如:
function itemEqual(item1, item2) {
// Need here to do a /deep/ equality check between
// the two items, e.g.:
return (item1.id == item2.id &&
item1.propertyA == item2.propertyA &&
item1.propertyB == item2.propertyB)
}
您可以使用lodash的_.isEqual()
函数之类的东西来代替实现自己的itemEqual
来对对象进行一般的深度相等检查。
答案 1 :(得分:1)
在减速器中添加一个检查当前状态的检查。和有效载荷。如果有效负载值与您当前的状态相同,请不要覆盖该状态。但按原样返回当前状态。并且只有在值和引用确实发生更改的情况下,减速器才会启动。
like:
case 'XXXSTATE':
if(state.xxx.value === payload.value)
return state;
else
return {...state,itemsLoading: true};
也redux不仅检查引用,但这是另一个主题:)
答案 2 :(得分:0)
代码的问题是您要返回相同的值。 要触发更改,您必须返回一个新对象,而不是对现有值的引用。
webapps