Store: 🐞[BUG]: Dynamic Selectors get multiple reads when other state changes

Created on 25 Apr 2020  ·  4Comments  ·  Source: ngxs/store

Affected Package

The issue is caused by package @ngxs/store

Description

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.

🔥 Exception or Error

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());
  }
}

Environment

Libs:
"@angular/core": "8.2.14",
"@ngxs/store": "3.6.2",

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.

All 4 comments

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:

Was this page helpful?
0 / 5 - 0 ratings