Skip to content

Commit

Permalink
perf(store): do not run change detection whenever the selectors emits
Browse files Browse the repository at this point in the history
  • Loading branch information
arturovt committed Mar 2, 2022
1 parent 5b116c1 commit 85530c7
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 51 deletions.
8 changes: 7 additions & 1 deletion packages/store/src/internal/state-stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@ import { BehaviorSubject } from 'rxjs';

import { PlainObject } from '@ngxs/store/internals';

import { InternalNgxsExecutionStrategy } from '../execution/internal-ngxs-execution-strategy';

/**
* BehaviorSubject of the entire state.
* @ignore
*/
@Injectable()
export class StateStream extends BehaviorSubject<PlainObject> {
constructor() {
constructor(private _ngxsExecutionStrategy: InternalNgxsExecutionStrategy) {
super({});
}

next(value: PlainObject): void {
this._ngxsExecutionStrategy.leave(() => super.next(value));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('Selectors within templates causing ticks (https://github.com/ngxs/stor
class TestModule {}

it(
'should run change detection whenever the selector emits after asynchronous action has been completed',
'should not run change detection whenever the selector emits after asynchronous action has been completed',
freshPlatform(async () => {
// Arrange
const { injector } = await skipConsoleLogging(() =>
Expand All @@ -68,7 +68,7 @@ describe('Selectors within templates causing ticks (https://github.com/ngxs/stor

// Assert
try {
expect(spy.mock.calls.length).toBeGreaterThan(count);
expect(spy).toHaveBeenCalledTimes(3);
} finally {
spy.mockRestore();
}
Expand Down
56 changes: 8 additions & 48 deletions packages/store/tests/zone.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,53 +25,10 @@ describe('zone', () => {
}
}

describe('[store.select]', () => {
it('should be performed inside Angular zone', () => {
let ticks = 0;

class MockApplicationRef extends ApplicationRef {
public tick(): void {
ticks++;
}
}

TestBed.configureTestingModule({
imports: [NgxsModule.forRoot([CounterState])],
providers: [
{
provide: ApplicationRef,
useClass: MockApplicationRef
}
]
});

const store: Store = TestBed.inject(Store);
const zone: NgZone = TestBed.inject(NgZone);

// NGXS performes initializions inside Angular zone
// thus it causes app to tick
expect(ticks).toBeGreaterThan(0);

zone.runOutsideAngular(() => {
store
.select<number>(({ counter }) => counter)
.pipe(take(3))
.subscribe(() => {
expect(NgZone.isInAngularZone()).toBeTruthy();
});

store.dispatch(new Increment());
store.dispatch(new Increment());
});

// Angular has run change detection 5 times
expect(ticks).toBe(5);
});
});

// =============================================================
it('"select" should be performed inside Angular zone', () => {
let ticks = 0;
let selectCallsInAngularZone = 0;

class MockApplicationRef extends ApplicationRef {
public tick(): void {
Expand All @@ -94,22 +51,25 @@ describe('zone', () => {

// NGXS performes initializions inside Angular zone
// thus it causes app to tick
expect(ticks).toBeGreaterThan(0);
expect(ticks).toEqual(4);

zone.runOutsideAngular(() => {
store
.select<number>(({ counter }) => counter)
.pipe(take(3))
.subscribe(() => {
expect(NgZone.isInAngularZone()).toBeTruthy();
if (NgZone.isInAngularZone()) {
selectCallsInAngularZone++;
}
});

store.dispatch(new Increment());
store.dispatch(new Increment());
});

// Angular has run change detection 5 times
expect(ticks).toBe(5);
// Angular has run change detection 7 times
expect(ticks).toBe(7);
expect(selectCallsInAngularZone).toEqual(3);
});

it('"select" should be performed outside Angular zone', () => {
Expand Down

0 comments on commit 85530c7

Please sign in to comment.