角度& NGRX防止选择器在值相等时在状态更改时发出相同的值 [英] Angular & NGRX prevent selector from emitting identical value on state change when values are equal

查看:76
本文介绍了角度& NGRX防止选择器在值相等时在状态更改时发出相同的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一种解决方案,以使选择器仅在与上次发出的值相比发生变化时才发出新值,并且不仅更改对存储的引用.

I'm searching for a solution to make my selector only emit a new value when the it has changed compared to the last emitted value, and not only the reference to the store is changed.

我的商店中有以下状态:

I have the following state in my store:

{
   items: [],
   loading: false,
   selectedItemId: 1
}

我有以下选择器:

export const getSelectedItem = createSelector(getItemsState,
    (state) => {
        return state.selectedItemId === null ? null : state.items.find(item => item.id === state.selectedItemId)
    }
);

当我在此选择器上进行订阅时,每次例如在商店中的加载标志发生更改时,都会触发该事件.我希望选择器仅在选定项目的值发生更改时才发出一个值. IE.从1到2.但是当状态对象的引用不同时,则不是.

When I do a subscribe on this selector this fires each time when for example the loading flag in the store changes. I would like the selector to emit only a value when the value of the selected item has changed. I.e. from 1 to 2. But not when the reference of the state object is different.

我为此找到了一个解决方案:

I found one solution for my problem with doing this:

this.itemStore.select(getSelectedItem).distinctUntilChanged((x, y) => {
    return x.id === y.id;
}).subscribe(item => {
    // do something
});

但是,我想移动逻辑以使更新的内容与我的选择器区分开,而不是在调用方使用.

However, I would like to move the logic to make the updated distinct into my selector instead of having this on the calling side.

我理解为什么会有这种行为,因为该框架仅检查对象是否相等,并且存储引用会更改,因为每次其中的任何更改我们都会从化简器中返回一个新的ItemState对象.但是我无法找到解决问题的方法,而且我无法想象我是唯一需要选择器的人,选择器仅在更改了有效值后才会更新.

I understand why I have this behavior as the framework only checks for object equality and the store reference changes as we return a new ItemState object from the reducer each time something changes in there. But I’m unable to find a solution to my problem, and I cannot imagine that I’m the only person in need of a selector which updates only when the effective value has been changed.

reducer的代码如下:

The code for the reducer looks like this:

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
    }
}

推荐答案

对于当前的减速器,distinctUntilChanged是处理此问题的正确运算符.解决此问题的另一种方法是,使您的reducer在更新项目时检测对象是否功能上不变",例如:

With your current reducer, distinctUntilChanged is the correct operator to handle this. Another way to address this would be by making your reducer detect whether the object is "functionally unchanged" when updating the items, for example, something like:

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来检查深度逻辑相等性.在这里,您可以使用功能上相等"的含义来确定项目是否已更改,例如:

You would need to implement itemEqual somehow to check for deep logical equality. This is where you use your knowledge of what, functionally, "equal" means to decide whether the item has changed, for example:

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来对对象进行一般的深度相等性检查.

You could use something like lodash's _.isEqual() function instead of implementing your own itemEqual for a general deep equality check on the objects.

这篇关于角度& NGRX防止选择器在值相等时在状态更改时发出相同的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆