I am experiencing strange behavior with my app. I have subscribed to a state property using the selector and what I am seeing is, my subscription is getting called no matter what property in the state changes.
Below is a cleaned-up version of my code. My state has all kinds of properties, some objects, and some flat properties. Selectors for all properties work as expected except `getImportStatus` and `getImportProgress` selectors. **Subscription to these selectors is triggered no matter what property in the store changes.** I am just about to lose my mind. Can anyone suggest what I am doing wrong? Has anyone faced such issue? I know people run into similar issues when they do not unsubscribe the subscriptions. But, in my case, as you can see I am unsubscribing and the event is being triggered for any property change which has got me puzzled. Here's my reducer: import {ImportConfigActions, ImportConfigActionTypes} from '../actions'; import * as _ from 'lodash'; import {ImportProgress} from '../../models/import-progress'; import {ImportStatus} from '../../models/import-status'; import {ActionReducerMap, createFeatureSelector} from '@ngrx/store'; export interface ImportState { importConfig: fromImportConfig.ImportConfigState; } export const reducers: ActionReducerMap<ImportState> = { importConfig: fromImportConfig.reducer, }; export const getImportState = createFeatureSelector<ImportState>('import'); export interface ImportConfigState { spinner: boolean; importStatus: ImportStatus; // This is my custom model importProgress: ImportProgress; // This is my custom model } export const initialState: ImportConfigState = { spinner: false, importStatus: null, importProgress: null }; export function reducer(state = initialState, action: ImportConfigActions): ImportConfigState { let newState; switch (action.type) { case ImportConfigActionTypes.ShowImportSpinner: newState = _.cloneDeep(state); newState.spinner = false; return newState; case ImportConfigActionTypes.HideImportSpinner: newState = _.cloneDeep(state); newState.spinner = false; return newState; case ImportConfigActionTypes.FetchImportStatusSuccess: newState = _.cloneDeep(state); newState.importStatus = action.importStatus; return newState; case ImportConfigActionTypes.FetchImportProgressSuccess: newState = _.cloneDeep(state); newState.importProgress = action.importProgress; return newState; default: return state; } } Here're my actions: import {Action} from '@ngrx/store'; import {ImportStatus} from '../../models/import-status'; import {ImportProgress} from '../../models/import-progress'; export enum ImportConfigActionTypes { ShowImportSpinner = '[Import Config] Show Import Spinner', HideImportSpinner = '[Import Config] Hide Import Spinner', FetchImportStatus = '[Import Config] Fetch Import Status', FetchImportStatusSuccess = '[ImportConfig] Fetch Import Status Success', FetchImportStatusFailure = '[Import Config] Fetch Import Status Failure', FetchImportProgress = '[Import Config] Fetch Import Progress', FetchImportProgressSuccess = '[ImportConfig] Fetch Import Progress Success', FetchImportProgressFailure = '[Import Config] Fetch Import Progress Failure' } export class ShowImportSpinner implements Action { readonly type = ImportConfigActionTypes.ShowImportSpinner; } export class HideImportSpinner implements Action { readonly type = ImportConfigActionTypes.HideImportSpinner; } export class FetchImportStatus implements Action { readonly type = ImportConfigActionTypes.FetchImportStatus; constructor(readonly projectId: number, readonly importId: number) {} } export class FetchImportStatusSuccess implements Action { readonly type = ImportConfigActionTypes.FetchImportStatusSuccess; constructor(readonly importStatus: ImportStatus) {} } export class FetchImportStatusFailure implements Action { readonly type = ImportConfigActionTypes.FetchImportStatusFailure; } export class FetchImportProgress implements Action { readonly type = ImportConfigActionTypes.FetchImportProgress; constructor(readonly projectId: number, readonly importId: number) {} } export class FetchImportProgressSuccess implements Action { readonly type = ImportConfigActionTypes.FetchImportProgressSuccess; constructor(readonly importProgress: ImportProgress) {} } export class FetchImportProgressFailure implements Action { readonly type = ImportConfigActionTypes.FetchImportProgressFailure; } export type ImportConfigActions = ShowImportSpinner | HideImportSpinner | FetchImportStatus | FetchImportStatusSuccess | FetchImportStatusFailure | FetchImportProgress | FetchImportProgressSuccess | FetchImportProgressFailure; Here're my effects: import {Injectable} from '@angular/core'; import {Actions, Effect, ofType} from '@ngrx/effects'; import {ImportConfigService} from '../../services'; import {from, Observable} from 'rxjs'; import {Action} from '@ngrx/store'; import { FetchImportProgress, FetchImportProgressFailure, FetchImportProgressSuccess, FetchImportStatus, FetchImportStatusFailure, FetchImportStatusSuccess, HideImportSpinner, ImportConfigActionTypes, StartImport } from '../actions'; import {catchError, map, mergeMap, switchMap} from 'rxjs/operators'; @Injectable() export class ImportConfigEffects { constructor(private actions$: Actions, private service: ImportConfigService, private errorService: ErrorService) {} @Effect() startImport: Observable<Action> = this.actions$.pipe( ofType<StartImport>(ImportConfigActionTypes.StartImport), switchMap((action) => { return this.service.startImport(action.payload.projectId, action.payload.importId, action.payload.importConfig) .pipe( mergeMap((res: any) => { if (res.status === 'Success') { return [ new HideImportSpinner() ]; } return []; }), catchError(err => from([ new HideImportSpinner() ])) ); }) ); @Effect() fetchImportStatus: Observable<Action> = this.actions$.pipe( ofType<FetchImportStatus>(ImportConfigActionTypes.FetchImportStatus), switchMap((action) => { return this.service.fetchImportStatus(action.projectId, action.importId) .pipe( mergeMap((res: any) => { if (res.status === 'Success') { return [ new FetchImportStatusSuccess(res.data) ]; } }), catchError(err => from([ new FetchImportStatusFailure() ])) ); }) ); @Effect() fetchImportProgress: Observable<Action> = this.actions$.pipe( ofType<FetchImportProgress>(ImportConfigActionTypes.FetchImportProgress), switchMap((action) => { return this.service.fetchImportProgress(action.projectId, action.importId) .pipe( mergeMap((res: any) => { if (res.status === 'Success') { return [ new FetchImportProgressSuccess(res.data) ]; } }), catchError(err => from([ new FetchImportProgressFailure() ])) ); }) ); } Here're my selectors: import {createSelector} from '@ngrx/store'; import {ImportConfig} from '../../models/import-config'; import {ImportConfigState} from '../reducers/import-config.reducer'; import {getImportState, ImportState} from '../reducers'; export const getImportConfigState = createSelector( getImportState, (importState: ImportState) => importState.importConfig ); export const getImportConfig = createSelector( getImportConfigState, (importConfigState: ImportConfigState) => importConfigState.importConfig ); export const isImportSpinnerShowing = createSelector( getImportConfigState, (importConfigState: ImportConfigState) => importConfigState.importSpinner ); export const getImportStatus = createSelector( getImportConfigState, (importConfigState: ImportConfigState) => importConfigState.importStatus ); export const getImportProgress = createSelector( getImportConfigState, (importConfigState: ImportConfigState) => importConfigState.importProgress ); Here's my component: import {Component, OnDestroy, OnInit, ViewEncapsulation} from '@angular/core'; import {select, Store} from '@ngrx/store'; import {ImportState} from '../../store/reducers'; import {library} from '@fortawesome/fontawesome-svg-core'; import {faAngleLeft, faAngleRight, faExchangeAlt, faFolder, faFolderOpen, faFileImport, faLink, faEquals, faCogs, faExclamationCircle, faFilter, faSearch, faHome} from '@fortawesome/free-solid-svg-icons'; import {faFile} from '@fortawesome/free-regular-svg-icons'; import {FetchImportProgress, FetchImportStatus} from '../../store/actions'; import {ActivatedRoute} from '@angular/router'; import {Subject} from 'rxjs'; import {BsModalRef, BsModalService} from 'ngx-bootstrap'; import {ImportProgressComponent} from '../import-progress/import-progress.component'; import {getImportStatus} from '../../store/selectors'; import {filter, map, takeUntil} from 'rxjs/operators'; import {ImportStatus} from '../../models/import-status'; @Component({ selector: 'app-import', templateUrl: './import.component.html', styleUrls: ['./import.component.scss'], encapsulation: ViewEncapsulation.None }) export class ImportComponent implements OnInit, OnDestroy { importId: string; projectId: string; status: number; phase: number; private importProgressModalRef: BsModalRef; private isProgressModalShowing = false; private unsubscribe$ = new Subject<void>(); queryParamsSubscription: any; constructor( private store: Store<ImportState>, private route: ActivatedRoute, private modalService: BsModalService) { library.add( faHome, faFolder, faFolderOpen, faFile, faFileImport, faAngleRight, faAngleLeft, faFilter, faSearch, faExchangeAlt, faLink, faEquals, faCogs, faExclamationCircle); this.queryParamsSubscription = this.route.queryParams .subscribe(params => { this.importId = params['importId']; this.projectId = params['projectId']; }); } ngOnInit(): void { this.store.dispatch(new FetchImportStatus(+this.projectId, +this.importId)); this.store.dispatch(new FetchImportProgress(+this.projectId, +this.importId)); this.store.pipe(select(getImportStatus), takeUntil(this.unsubscribe$), map((importStatus: ImportStatus) => importStatus), filter((importStatus: ImportStatus) => !!importStatus)) .subscribe((importStatus: ImportStatus) => { this.status = importStatus.status; // This is getting triggered for all property changes this.phase = importStatus.phase; this.handleStatusChange(); }); } ngOnDestroy(): void { this.unsubscribe$.next(); this.unsubscribe$.complete(); this.queryParamsSubscription.unsubscribe(); } handleStatusChange() { if (this.status !== 2 || (this.phase === 5)) { if (!this.isProgressModalShowing) { this.openImportProgressModal(); this.isProgressModalShowing = true; } } } openImportProgressModal() { this.importProgressModalRef = this.modalService.show(ImportProgressComponent, Object.assign({}, { class: 'modal-md', ignoreBackdropClick: true })); this.importProgressModalRef.content.modalRef = this.importProgressModalRef; this.importProgressModalRef.content.onModalCloseCallBack = this.onImportProgressModalClose; } onImportProgressModalClose = () => { this.isProgressModalShowing = false; }; } -- You received this message because you are subscribed to the Google Groups "Angular and AngularJS discussion" group. To unsubscribe from this group and stop receiving emails from it, send an email to angular+unsubscr...@googlegroups.com. To post to this group, send email to angular@googlegroups.com. Visit this group at https://groups.google.com/group/angular. To view this discussion on the web visit https://groups.google.com/d/msgid/angular/b1483ddd-dd11-45a5-a1cf-cc45036226b1%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.