Search code examples
angularrxjsngrxrxjs-observables

Is it necessary to subscribe and remove observables it in these cases?


I'm here thinking about observables ..

And I imagined the following situations:

Example 1) In my case when I use NGRX I create all the architecture correctly, and I create a selector service for that particular store.

In the service it is not possible to use ngOnDestroy because it is not a component and I have the following question, is there a memory leak in a service? Or does this service automatically destroy observables?

Example 2) Using the selectors in example one, is it necessary to subscribe to this selector and then destroy it?

PEOPLE-SELECTORS.SERVICE

@Injectable({ providedIn: 'root' })
export class PeopleSelectorsService {
    constructor(private readonly store: Store<StoreState>) {}

    get error(): Observable<IRequestError> {
        return this.store.pipe(select(fromPeopleSelectors.getError));
    }

    get loading(): Observable<boolean> {
        return this.store.pipe(select(fromPeopleSelectors.getLoading));
    }

    get main(): Observable<IPeople> {
        return this.store.pipe(select(fromPeopleSelectors.getMain));
    }

    get total(): Observable<number> {
        return this.store.pipe(select(fromPeopleSelectors.selectTotal));
    }

    get all(): Observable<Array<IPeople>> {
        return this.store.pipe(select(fromPeopleSelectors.selectAll));
    }

    get allIds(): Observable<Array<string | number>> {
        return this.store.pipe(select(fromPeopleSelectors.selectIds));
    }
}

APP.COMPONENT

    ngOnInit(): void {
        this.peopleDispatchService.getAll();
        this.isLoading$ = this.peopleSelectorsService.loading;
}

<main [attr.isLoading]="isLoading$ | async">
    <ng-container></ng-container>
    <app-loading-container *ngIf="isLoading$ | async; else isMainController"></app-loading-container>

    <ng-template #isMainController>
        <app-user-talk-controller-container></app-user-talk-controller-container>
        <app-user-talk-container></app-user-talk-container>
    </ng-template>
</main>

Solution

  • That's totally normal. Until you have a subscription you don't have any memory leaks. async pipe knows when to subscribe and when to unsubscribe so you shouldn't worry about forgotten subscriptions, async will unsubscribe once the subscription isn't need anymore.

    The best way is to write code full of pipes without any subscription IMHO.

    For example in your case I would create a single observable in the app.component.ts that combines all data together and has a single observable.

    ngOnInit(): void {
      this.data$ = combineLatest(
        this.peopleSelectorsService.loading,
        this.peopleSelectorsService.all,    
      ).map(([isLoading, records]) => ({isLoading, records}));
    }
    
    <ng-container *ngIf="data$ | async as data">
    <main [attr.isLoading]="data.isLoading">
        <ng-container></ng-container>
        <app-loading-container *ngIf="data.isLoading; else isMainController"></app-loading-container>
    
        <ng-template #isMainController>
            <app-user-talk-controller-container></app-user-talk-controller-container>
            <app-user-talk-container [records]="data.records"></app-user-talk-container>
        </ng-template>
    </main>
    </ng-container>
    

    the main benefit I see is that we have single subscription and there's no problem of subscription intersection, because time to time in template when you have several nested async you can find that they can depend on each other too and they won't work properly. with a single subscription there's no such a problem.

    Also that's what we do in our current project. time to time data$ is quite big, but still it feels better then several subscriptions in a template or in a class.