You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
exportfunctionuseObservableInternal(...): Observable<TOutput>{if(!inputs){returnuseState(initas()=>Observable<TOutput>)[0]}const[inputs$]=useState(()=>newBehaviorSubject(inputs))const[source$]=useState(()=>init(inputs$))constfirstEffectRef=useRef(true)useCustomEffect(()=>{if(firstEffectRef.current){firstEffectRef.current=falsereturn}inputs$.next(inputs)},inputs)// No `inputs$.complete()` presents :/returnsource$}
When using operators like shareReplay(), they typically rely on the source observable(i.e. the BehaviorSubject inside useObservable()) to send a complete signal in order to unsubscribe the internal ReplaySubject from it. If no complete signal is received, resource leaks could occur.
The example above is using RxJS 6. Haven't tested on RxJS 7 yet.
In the example, we use a toggle button to control the existence of FantasyGauge.
In FantasyGauge we use a modified version of useObservable with myShareReplay (which just logs some actions on top of original shareReplay logics from RxJS 6.2.1).
When FantasyGauge is destroyed, the BehaviorSubject of myUseObservable still holds some observers. See BEFORE.mp4 below.
When added inputs$.complete(), the BehaviorSubject no longer holds any observers. See AFTER.mp4 below.
I also find that useObservableRef and useObservableCallback both use BehaviorSubject or Subject under the hood, and both of them don't seem to call complete() either. So I suspect these hooks might also be vulnerable to resource leaks.
Root cause:
When using operators like
shareReplay(), they typically rely on the source observable(i.e. theBehaviorSubjectinsideuseObservable()) to send acompletesignal in order to unsubscribe the internalReplaySubjectfrom it. If nocompletesignal is received, resource leaks could occur.Stackblitz URL: https://stackblitz.com/edit/stackblitz-starters-ghtjf1?devToolsHeight=33&file=src%2FApp.tsx
The example above is using RxJS 6. Haven't tested on RxJS 7 yet.
FantasyGauge.FantasyGaugewe use a modified version ofuseObservablewithmyShareReplay(which just logs some actions on top of originalshareReplaylogics from RxJS 6.2.1).FantasyGaugeis destroyed, the BehaviorSubject ofmyUseObservablestill holds someobservers. See BEFORE.mp4 below.inputs$.complete(), the BehaviorSubject no longer holds anyobservers. See AFTER.mp4 below.I also find that
useObservableRefanduseObservableCallbackboth useBehaviorSubjectorSubjectunder the hood, and both of them don't seem to callcomplete()either. So I suspect these hooks might also be vulnerable to resource leaks.BEFORE.mp4
AFTER.mp4