useEventSource: onopen/onerror/onmessage never check if the connection was superseded, unlike useWebSocket after #5573
Describe the bug
useEventSource's _init() sets up es.onopen, es.onerror, and es.onmessage (plus the per-named-event listeners) without ever checking whether es is still the current connection (eventSource.value === es). useWebSocket just got exactly this guard added to its last unguarded handler in #5573 (onopen/onclose already had it, onmessage was the gap) — useEventSource has none of the three.
Reproduction
packages/core/useEventSource/index.ts, current _init():
const es = new EventSource(urlRef.value, { withCredentials })
status.value = 'CONNECTING'
eventSource.value = es
es.onopen = () => {
status.value = 'OPEN'
error.value = null
}
es.onerror = (e) => {
status.value = 'CLOSED'
error.value = e
// ... reconnect logic calls setTimeout(_init, delay), creating a new `es`
}
es.onmessage = (e: MessageEvent) => {
event.value = null
data.value = serializer.read(e.data) ?? null
lastEventId.value = e.lastEventId
}
None of these three handlers (nor the useEventListener(es, event_name, ...) registered per named event a few lines below) check eventSource.value === es before writing to status/error/data/event/lastEventId.
Because _init() is re-entrant (called again by open() when the reactive url changes, and by the autoReconnect path via setTimeout(_init, delay)), it's possible for an old EventSource (es) to still have an in-flight onmessage/onerror queued when a new one is created and assigned to eventSource.value. That stale event still fires and overwrites data/status/error with a value from a connection that's already superseded, exactly the class of bug #5573 fixed for useWebSocket's onmessage (see its PR description: "A reconnect closes the old socket and swaps in a new one, and any message the old socket still delivers before its close handshake finishes ends up writing to data").
Expected behavior
Same guard useWebSocket now has on all three of its equivalent handlers, applied to useEventSource's onopen/onerror/onmessage and the named-event listeners:
es.onmessage = (e: MessageEvent) => {
if (eventSource.value !== es)
return
event.value = null
data.value = serializer.read(e.data) ?? null
lastEventId.value = e.lastEventId
}
System Info
Checked against current main (packages/core/useEventSource/index.ts), compared with useWebSocket's fix in #5573.
Additional context
useWebSocket and useEventSource share the same "reconnect swaps the underlying connection object" shape, so I'd expect the fix to look basically identical to #5573's diff, just applied to EventSource's three handlers plus the dynamic useEventListener loop instead of WebSocket's.
Source: vueuse/vueuse