Overview
This guide documents the approved patterns for managing RxJS subscriptions in the CometChat Angular V5 UIKit. Following these patterns ensures that subscriptions are automatically cleaned up when components and services are destroyed, preventing memory leaks in long-running sessions.✅ Approved Patterns
1. takeUntilDestroyed() — Primary Pattern
The preferred pattern for all new subscriptions. Uses Angular’s DestroyRef to automatically unsubscribe when the component or service is destroyed.
- No boilerplate
ngOnDestroyneeded for subscription cleanup - Works in both components and services
- Angular manages the lifecycle automatically via
DestroyRef - Composable with other RxJS operators
2. toSignal() — For Service Observables
Use toSignal() when you need to consume a service observable as an Angular Signal in a component. The subscription is automatically cleaned up when the component is destroyed.
- Zero subscription management code
- Integrates with Angular’s change detection (OnPush compatible)
- Automatically handles initial value and completion
3. CometChat SDK Listeners — Service-Managed Pattern
CometChat SDK listeners (addMessageListener, addUserListener, etc.) must be registered and removed via the service layer, not directly in components.
- SDK listeners are singleton-scoped — they must persist across component destroy/recreate cycles (e.g., tab switching)
- Components should never call
cleanup()inngOnDestroy— this would tear down listeners on every navigation - Call
cleanup()only for application-level events: user logout, account switching
❌ Anti-Patterns to Avoid
❌ Manual destroy$ Subject
ngOnDestroy implementation. If next() or complete() is forgotten, subscriptions leak.
Fix: Use takeUntilDestroyed(this.destroyRef) instead.
❌ Manual Subscription.unsubscribe()
takeUntilDestroyed(this.destroyRef) — no property or ngOnDestroy needed.
❌ Subscribing Without Any Cleanup
takeUntilDestroyed(this.destroyRef) or toSignal().
Migration Guide
If you encounter the oldtakeUntil(this.destroy$) pattern, here is how to migrate:
Before
After
- Add
DestroyRefto@angular/coreimports - Add
takeUntilDestroyedto@angular/core/rxjs-interopimports - Add
private readonly destroyRef = inject(DestroyRef)field - Replace
.pipe(takeUntil(this.destroy$))with.pipe(takeUntilDestroyed(this.destroyRef)) - Remove
private destroy$ = new Subject<void>() - Remove
this.destroy$.next()andthis.destroy$.complete()fromngOnDestroy - Remove
OnDestroyinterface if no other cleanup is needed - Remove unused
SubjectandtakeUntilimports
Summary
| Pattern | Use Case | Cleanup |
|---|---|---|
takeUntilDestroyed(destroyRef) | Any RxJS subscription in component/service | Automatic via DestroyRef |
toSignal(obs$) | Service observable → Signal in component | Automatic via DestroyRef |
| Service-managed SDK listeners | CometChat SDK addXxxListener | Manual via cleanup() on logout |
takeUntil(destroy$) | ❌ Deprecated | Manual — avoid |
subscription.unsubscribe() | ❌ Deprecated | Manual — avoid |