The issue is caused by package @ngxs/store
Dynamic Selectors get multiple reads when other state changes.
-The dynamic selector is read each time any state is modified.
-The static(anotated with @Selector()) works correctly, and it's only read once if the current state doesn't change.
Test for reproduce the error, reading StaticState from different selectors.
import {TestBed} from '@angular/core/testing';
import {Action, createSelector, NgxsModule, Selector, State, StateContext, Store} from '@ngxs/store';
fdescribe('StaticState selectors recalculate when not needed', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
NgxsModule.forRoot([
AnotherState,
StaticState
])
]
});
});
it('get num once when updating different state', () => {
const spyStatic = jasmine.createSpy('callback');
const spyDinamic = jasmine.createSpy('callback');
const store = TestBed.get(Store) as Store;
store.select(StaticState.getStaticSelector).subscribe(spyStatic);
store.select(StaticState.getDinamicSelector).subscribe(spyDinamic);
store.dispatch(new UpdateAnotherState);
store.dispatch(new UpdateAnotherState);
expect(spyStatic).toHaveBeenCalledTimes(1); // Expected spy callback to have been called once. It was called 1 times.
expect(spyDinamic).toHaveBeenCalledTimes(1); // Expected spy callback to have been called once. It was called 3 times.
});
});
class UpdateAnotherState {
static type = 'UpdateAnotherState';
}
export interface AuxStateModel {
num: number
}
@State<AuxStateModel>({
name: 'staticState',
defaults: {num: 333}
})
class StaticState {
constructor() {
}
@Selector()
static getStaticSelector(state: AuxStateModel) {
return state.num;
}
public static getDinamicSelector() {
return createSelector([this], (state: AuxStateModel) => {
return state.num;
});
}
}
@State<number>({
name: 'another',
defaults: 999
})
class AnotherState {
constructor() {
}
@Action(UpdateAnotherState)
update(ctx: StateContext<number>) {
ctx.setState(Math.random());
}
}
Libs:
"@angular/core": "8.2.14",
"@ngxs/store": "3.6.2",
It looks like you are not actually using the dynamic selector but rather using the factory function is a selector.
Change this line:
store.select(StaticState.getDinamicSelector).subscribe(spyDinamic);
To this:
store.select(StaticState.getDinamicSelector()).subscribe(spyDinamic);
Note the actual invocation of the factory function.
What is the result after that change?
My fault, i was using it in a test and didn't check twice. It works ok after i fixed the test like you said. I check it also inside an angular App and it's ok, there isn't anything wrong.
The issue with the Entity-state, should be for a different reason.
PS. I wish I could give you a medal for your awesome repro with a test!!!!
That would be such a great start if there was an issue to solve :+1:
Most helpful comment
My fault, i was using it in a test and didn't check twice. It works ok after i fixed the test like you said. I check it also inside an angular App and it's ok, there isn't anything wrong.
The issue with the Entity-state, should be for a different reason.