useReq() has no teardown path once its query has settled
- Dominant language
- TypeScript
- Stars
- 29
- Forks
- 3
- Avg merge
- 3h 5m
- Merged PRs (30d)
- 11
Description
#60 closes a request's REQ when the query is cancelled mid-fetch, which is the
only teardown `useReq()` has. Once the query settles there is no longer anything
to cancel, so a subscription that outlives its consumer stays open.
This is reachable with a caller-supplied `req`. The oneshot req `useReq()` builds
for itself completes on EOSE and RxJS tears it down, but a forward req never
completes:
1. A component passes its own `RxReqBase` and the query resolves on the first event.
2. The component is destroyed. The query is already settled, so `removeObserver()`
has no in-flight retryer to cancel and the abort never fires.
3. The subscription stays live. Further `req.emit()` calls keep pushing events
through it and into `queryClient.setQueryData()` for a component that is gone.
Reactive re-invocation compounds it: components call the hooks from a reactive
statement (`$: result = useUniqueEventList(...)`), so each re-run builds a fresh
observable, and with the default `staleTime: 0` a remount refetches and subscribes
again — while the previous subscription is still running.
Fixing this needs a lifecycle hook `useReq()` does not have today. Rough options:
- Return an unsubscriber from `ReqResult` and have the components call it from
`onDestroy`. Explicit, but an API change for direct hook users.
- Tear down when the query's observer count drops to zero, via the query cache's
`observerRemoved` event. No API change, but Svelte's subscribe/unsubscribe
ordering across a reactive re-run can transiently hit zero observers, so it
needs care to avoid killing a subscription that is about to be re-adopted.
Worth noting while looking at this: `useReq()` calls `req.emit(filters)` before
anything subscribes to the query, so for a forward req that first emission is
dropped and no REQ is sent until the caller emits again. That may deserve its own
issue.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reading the useReq() and ReqResult lifecycle, then inspect how query observers are removed and how onDestroy or the query cache's observerRemoved event could participate. Reproduce the forward-req case where the query settles before the component is destroyed. Done means subscriptions are torn down when consumers leave and later req.emit() calls no longer update the removed component's query.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100