-
Notifications
You must be signed in to change notification settings - Fork 45
Open
Description
Root cause:
export function useObservableInternal(...): Observable<TOutput> {
if (!inputs) {
return useState(init as () => Observable<TOutput>)[0]
}
const [inputs$] = useState(() => new BehaviorSubject(inputs))
const [source$] = useState(() => init(inputs$))
const firstEffectRef = useRef(true)
useCustomEffect(() => {
if (firstEffectRef.current) {
firstEffectRef.current = false
return
}
inputs$.next(inputs)
}, inputs)
// No `inputs$.complete()` presents :/
return source$
}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.
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.
- In the example, we use a toggle button to control the existence of
FantasyGauge. - In
FantasyGaugewe use a modified version ofuseObservablewithmyShareReplay(which just logs some actions on top of originalshareReplaylogics from RxJS 6.2.1). - When
FantasyGaugeis destroyed, the BehaviorSubject ofmyUseObservablestill holds someobservers. See BEFORE.mp4 below. - When added
inputs$.complete(), the BehaviorSubject no longer holds anyobservers. 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.
BEFORE.mp4
AFTER.mp4
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels