My normalized ngrx store looks like this:
export interface State {
carts: EntityState<Cart>;
items: EntityState<Item>;
}
export interface Cart {
id: number;
maxVolume: number;
}
export interface Item {
id: number;
cartId: number;
volume: number;
}
It's a pretty basic setup where a cart can contain several items. My selector needs to return an array with all the carts with arrays containing their items, but also compute if items are in danger of dropping out of their respective cart:
export const select: MemoizedSelector<object, any> = createSelector(
selectAllCarts, selectAllItems,
(allCarts: Cart[], allItems: Item[]) => {
return allCarts.map(c => {
const items = allItems.filter(i => i.cartId == i.id);
return {
id: c.id,
items: items.map(i => {
// computations, should not run if cart's items have not changed
// needs to be memoized
const computed = isCartOverfilled(i, c, items);
return {
id: i.id,
mightFallOut: computed//computed value needs to be output
}
})
}
});
});
Every time an item updates, isCartOverfilled will run for each item in the store. But, isCartOverfilled is potentially expensive and depends only on the items inside a cart. When an item updates, e.g. gets added to a cart, isCartOverfilled should execute ONLY for the items inside, i.e. memoized by cart id.
How do I achieve this?
I have tried selecting items from a single cart:
export const selectCart = (cartId: number) => createSelector(
selectItemsByCartId(cartId), selectCartById(cartId),
(items: Item[], cart: Cart) => {
return {
id: cart.id,
items: items.map(i => {
const computed = isCartOverfilled(i, cart, items);
return {
id: i.id,
mightFallOut: computed
}
})
}
});
This selector would not excessively compute, but I need all the carts, and I'm not sure if it's doable with a selector.