live demo https://stackblitz.com/edit/angular-vw78jf
There is ToppingsStateModel in my ngxs state
export interface ToppingsStateModel {
entities: { [ id: number ]: Topping };
selectedToppings: number[];
}
One action change my entities list, another action change selectedToppings. In products.component I get list of toppings from selector
export class ToppingsState {
constructor(private toppingsService: ToppingsService) {
}
@Selector()
static entities(state: ToppingsStateModel) {
console.log('getEntities', state.entities);
return state.entities;
}
@Selector([ToppingsState.entities])
static toppings(state: ToppingsStateModel, entities: {[id: number]: Topping}): Topping[] {
return Object.keys(entities).map(id => entities[parseInt(id, 10)]);
}
...
}
and it's product.component
export class ProductsComponent implements OnInit {
@Select(ToppingsState.toppings) toppings$: Observable<Topping[]>;
constructor(private store: Store, private actions$: Actions) {}
ngOnInit() {
const state = this.store.dispatch(new LoadToppings());
setTimeout(() => this.store.dispatch(new VisualizeToppings([1])), 2000);
this.toppings$.subscribe((toppings) => {console.log('UUUU NEW TOPPINGS!!!')});
}
}
when I dispatch VisualizeToppings action I get new toppings value. In my console I have
action [Products] Load Toppings @ 10:57:59.735
getEntities {}
UUUU NEW TOPPINGS!!!
getEntities {1: {…}, 2: {…}}
UUUU NEW TOPPINGS!!!
action [Products] Visualize Toppings @ 10:58:01.744
getEntities {1: {…}, 2: {…}}
UUUU NEW TOPPINGS!!!
I changed another part of the state. Why did selectors execute again when I dispatched VisualizeToppings action? What do I do wrong?
There is a known issue with @Selector
where the model of the container state class is always assumed as the first parameter. See: https://github.com/ngxs/store/issues/386#issuecomment-395780734
This is your problem... Because of this first parameter, your selector has a dependency on the state model ToppingsStateModel
and the specified ToppingsState.entities
selector.
@Selector([ToppingsState.entities])
static toppings(state: ToppingsStateModel, entities: {[id: number]: Topping}): Topping[] {
return Object.keys(entities).map(id => entities[parseInt(id, 10)]);
}
This results in the selector being recalculated when any part of the ToppingsStateModel changes. As a workaround you can move the selector to another class that is not a State class and remove the first parameter. I refer to these as Query classes. This is known as a meta-selector see the docs here: https://ngxs.gitbook.io/ngxs/concepts/select#meta-selectors
This will be fixed as part of the breaking changes in NGXS v4 (see https://github.com/ngxs/store/issues/827) and there is currently a PR for a feature flag to change this behaviour before NGXS v4 arrives. See: https://github.com/ngxs/store/pull/858
I hope this helps and explains the issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With