Incomplete documentation of `@ngrx/no-store-subscription` ESLint rule
Information
Currently documentation doesn't cover the use cases where you need to use fetched store value in component code itself.
https://ngrx.io/guide/eslint-plugin/rules/no-store-subscription
Like in that doc page how to make that this.items to work with here without errors.
Documentation page
https://ngrx.io/guide/eslint-plugin/rules/no-store-subscription
I would be willing to submit a PR to fix this issue
- [ ] Yes
- [ ] No
Hi @tarlepp can you please elaborate more on this topic. What exactly needs to be done? Do you want to enhance the documentation by providing more examples of "Using the async pipe is preferred over store subscription"
Can you please elaborate, I would love to contribute to this issue and raise a PR.
Some recommendations from my side to be added in the documentation page :
For the example 1 :
⚠️ Problems when using store subscription:
-
This creates a manual subscription.
-
Unless you unsubscribe (e.g., using takeUntil, async, or destroyRef in Angular 16+), it will cause a memory leak because the subscription stays alive even after the component is destroyed.
-
Also, it makes change detection a bit more manual since you assign values yourself.
👉 When to use:
-
Only when you need the value inside component logic (not just for display), e.g., for conditionals, API calls, form patching, navigation, etc.
-
If used, make sure to clean it up.
For the example 2:
✅ Preferred approach
-
No manual subscription, Angular’s async pipe handles subscription + unsubscription automatically.
-
Keeps the component more declarative, easier to test, and reduces boilerplate.
-
Recommended in NgRx docs for template binding.
👉 When to use:
- Whenever the value is only needed for display in the template.
I wanted to answer on this issue, but I forgot about it. The example on the page shows what is desired, but doesn't mention why.
As you already mentioned, we want to prevent a manual subscription on an Observable. Instead, we prefer to use the async pipe.
@emDevanshu if you want, please feel free to extend the docs by adding some content text.
Sure @timdeschryver will make the changes !!!!
I could use an example of how it's recommended to subscribe to state changes for a feature when the response is solely within the component's code. For example, I use the following:
this.authenticatedUserSubscription = this.store
.select(selectAuthenticatedUserState)
.subscribe(state => {
this.busy = state.busy;
if (state.authenticated) {
this.logger.logDebug('Already authenticated: sending to home');
this.router.navigateByUrl('/');
}
});
to redirect away from our login page when it's determined that the user is already authenticated. Now, with Angular 20, the above causes an error during linting:
55:8 error `Store` subscription is forbidden. Use the `async` pipe instead. (https://ngrx.io/guide/eslint-plugin/rules/no-store-subscription) @ngrx/no-store-subscription
But there is no example for how to do the above otherwise.
@mcpierce sorry for my late response.
This rule is to prevent memory leaks that are creating from store subscriptions.
Using the async pipe instead prevents this.
The provided example can use the tap operator, which will handle the navigation, instead of subscribing. Another solution is to use an effect for this instead.
@mcpierce sorry for my late response. This rule is to prevent memory leaks that are creating from store subscriptions. Using the
asyncpipe instead prevents this.The provided example can use the
tapoperator, which will handle the navigation, instead of subscribing. Another solution is to use aneffectfor this instead.
So would it be something like this then?
this.store.select(selectAuthenticatedUserState)
.tap((state) => {
this.busy = state.busy;
if (state.authenticated) {
this.logger.logDebug('Already authenticated: sending to home');
this.router.navigateByUrl('/');
}
});
@mcpierce yes, indeed.