Skip to content

⬅️ Back to Table of Contents

📄 useEventSource

📊 Analysis Summary

Metric Count
🔧 Functions 6
📦 Imports 9
📊 Variables & Constants 1
🟢 Vue Composition API 1
📐 Interfaces 2
📑 Type Aliases 1

📚 Table of Contents

🛠️ File Location:

📂 packages/core/useEventSource/index.ts

📦 Imports

Name Source
Fn @vueuse/shared
MaybeRefOrGetter vue
ShallowRef vue
isClient @vueuse/shared
toRef @vueuse/shared
tryOnScopeDispose @vueuse/shared
shallowRef vue
watch vue
useEventListener ../useEventListener

Variables & Constants

Name Type Kind Value Exported
DEFAULT_EVENT "message" const 'message'

Vue Composition API

Name Type Reactive Variables Composables
watch watch none none

Functions

useEventSource(url: MaybeRefOrGetter<string | URL | undefin…, events: Events, options: UseEventSourceOptions<Data>): UseEventSourceReturn<Events, Data>

Reactive wrapper for EventSource.

Parameters:

  • url any: No description
  • events any: No description
  • options any: No description

See: https://vueuse.org/useEventSource, https://developer.mozilla.org/en-US/docs/Web/API/EventSource/EventSource EventSource

Raw JSDoc
/**
 * Reactive wrapper for EventSource.
 *
 * @see https://vueuse.org/useEventSource
 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventSource/EventSource EventSource
 * @param url
 * @param events
 * @param options
 */

Calls:

  • shallowRef (from vue)
  • toRef (from @vueuse/shared)
  • eventSource.value.close
  • es.close
  • resolveNestedOptions
  • setTimeout
  • retries
  • onFailed
  • useEventListener (from ../useEventListener)
  • serializer.read
  • close
  • _init
  • open
  • watch (from vue)
  • tryOnScopeDispose (from @vueuse/shared)

Internal Comments:

// only reconnect if EventSource isn't reconnecting by itself
// this is the case when the connection is closed (readyState is 2)

Code
export function useEventSource<Events extends string[], Data = any>(
  url: MaybeRefOrGetter<string | URL | undefined>,
  events: Events = [] as unknown as Events,
  options: UseEventSourceOptions<Data> = {},
): UseEventSourceReturn<Events, Data> {
  const event: ShallowRef<string | null> = shallowRef(null)
  const data: ShallowRef<Data | null> = shallowRef(null)
  const status = shallowRef<EventSourceStatus>('CONNECTING')
  const eventSource = shallowRef<EventSource | null>(null)
  const error = shallowRef<Event | null>(null)
  const urlRef = toRef(url)
  const lastEventId = shallowRef<string | null>(null)

  let explicitlyClosed = false
  let retried = 0

  const {
    withCredentials = false,
    immediate = true,
    autoConnect = true,
    autoReconnect,
    serializer = {
      read: (v?: string) => v as Data,
    },
  } = options

  const close = () => {
    if (isClient && eventSource.value) {
      eventSource.value.close()
      eventSource.value = null
      status.value = 'CLOSED'
      explicitlyClosed = true
    }
  }

  const _init = () => {
    if (explicitlyClosed || typeof urlRef.value === 'undefined')
      return

    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

      // only reconnect if EventSource isn't reconnecting by itself
      // this is the case when the connection is closed (readyState is 2)
      if (es.readyState === 2 && !explicitlyClosed && autoReconnect) {
        es.close()
        const {
          retries = -1,
          delay = 1000,
          onFailed,
        } = resolveNestedOptions(autoReconnect)
        retried += 1

        if (typeof retries === 'number' && (retries < 0 || retried < retries))
          setTimeout(_init, delay)
        else if (typeof retries === 'function' && retries())
          setTimeout(_init, delay)
        else
          onFailed?.()
      }
    }
    events = events.length > 0 ? events : [DEFAULT_EVENT] as unknown as Events
    for (const event_name of events) {
      useEventListener(es, event_name, (e: Event & { data?: string, lastEventId?: string }) => {
        event.value = event_name
        data.value = serializer.read(e.data) ?? null
        lastEventId.value = e.lastEventId ?? null
      }, { passive: true })
    }
  }

  const open = () => {
    if (!isClient)
      return
    close()
    explicitlyClosed = false
    retried = 0
    _init()
  }

  if (immediate)
    open()

  if (autoConnect)
    watch(urlRef, open)

  tryOnScopeDispose(close)

  return {
    eventSource,
    event,
    data,
    status,
    error,
    open,
    close,
    lastEventId,
  }
}

resolveNestedOptions(options: T | true): T

Parameters:

  • options T | true

Returns: T

Code
function resolveNestedOptions<T>(options: T | true): T {
  if (options === true)
    return {} as T
  return options
}

Internal helpers

Declared inside another function in this file.

read(v: string): Data

Parameters:

  • v string

Returns: Data

Code
(v?: string) => v as Data

close(): void

Returns: void

Calls:

  • eventSource.value.close
Code
() => {
    if (isClient && eventSource.value) {
      eventSource.value.close()
      eventSource.value = null
      status.value = 'CLOSED'
      explicitlyClosed = true
    }
  }

_init(): void

Returns: void

Calls:

  • es.close
  • resolveNestedOptions
  • setTimeout
  • retries
  • onFailed
  • useEventListener (from ../useEventListener)
  • serializer.read

Internal Comments:

// only reconnect if EventSource isn't reconnecting by itself
// this is the case when the connection is closed (readyState is 2)

Code
() => {
    if (explicitlyClosed || typeof urlRef.value === 'undefined')
      return

    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

      // only reconnect if EventSource isn't reconnecting by itself
      // this is the case when the connection is closed (readyState is 2)
      if (es.readyState === 2 && !explicitlyClosed && autoReconnect) {
        es.close()
        const {
          retries = -1,
          delay = 1000,
          onFailed,
        } = resolveNestedOptions(autoReconnect)
        retried += 1

        if (typeof retries === 'number' && (retries < 0 || retried < retries))
          setTimeout(_init, delay)
        else if (typeof retries === 'function' && retries())
          setTimeout(_init, delay)
        else
          onFailed?.()
      }
    }
    events = events.length > 0 ? events : [DEFAULT_EVENT] as unknown as Events
    for (const event_name of events) {
      useEventListener(es, event_name, (e: Event & { data?: string, lastEventId?: string }) => {
        event.value = event_name
        data.value = serializer.read(e.data) ?? null
        lastEventId.value = e.lastEventId ?? null
      }, { passive: true })
    }
  }

open(): void

Returns: void

Calls:

  • close
  • _init
Code
() => {
    if (!isClient)
      return
    close()
    explicitlyClosed = false
    retried = 0
    _init()
  }

Interfaces

UseEventSourceOptions<Data>

Interface Code
export interface UseEventSourceOptions<Data> extends EventSourceInit {
  /**
   * Enabled auto reconnect
   *
   * @default false
   */
  autoReconnect?: boolean | {
    /**
     * Maximum retry times.
     *
     * Or you can pass a predicate function (which returns true if you want to retry).
     *
     * @default -1
     */
    retries?: number | (() => boolean)

    /**
     * Delay for reconnect, in milliseconds
     *
     * @default 1000
     */
    delay?: number

    /**
     * On maximum retry times reached.
     */
    onFailed?: Fn
  }

  /**
   * Immediately open the connection when calling this composable
   *
   * @default true
   */
  immediate?: boolean

  /**
   * Automatically connect to the websocket when URL changes
   *
   * @default true
   */
  autoConnect?: boolean

  /**
   * Custom data serialization
   */
  serializer?: {
    read: (v?: string) => Data
  }
}

Properties

Name Type Optional Description
autoReconnect boolean \| { /** * Maximum retry times. * * Or you can pass a predicate funct... not shown
immediate boolean not shown
autoConnect boolean not shown
serializer { read: (v?: string) => Data } not shown

UseEventSourceReturn<Events extends string[], Data = any>

Interface Code
export interface UseEventSourceReturn<Events extends string[], Data = any> {
  /**
   * Reference to the latest data received via the EventSource,
   * can be watched to respond to incoming messages
   */
  data: ShallowRef<Data | null>

  /**
   * The current state of the connection, can be only one of:
   * 'CONNECTING', 'OPEN' 'CLOSED'
   */
  status: ShallowRef<EventSourceStatus>

  /**
   * The latest named event
   */
  event: ShallowRef<Events[number] | null>

  /**
   * The current error
   */
  error: ShallowRef<Event | null>

  /**
   * Closes the EventSource connection gracefully.
   */
  close: EventSource['close']

  /**
   * Reopen the EventSource connection.
   * If there the current one is active, will close it before opening a new one.
   */
  open: Fn

  /**
   * Reference to the current EventSource instance.
   */
  eventSource: ShallowRef<EventSource | null>
  /**
   * The last event ID string, for server-sent events.
   * @see https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/lastEventId
   */
  lastEventId: ShallowRef<string | null>
}

Properties

Name Type Optional Description
data ShallowRef<Data \| null> not shown
status ShallowRef<EventSourceStatus> not shown
event ShallowRef<Events[number] \| null> not shown
error ShallowRef<Event \| null> not shown
close EventSource['close'] not shown
open Fn not shown
eventSource ShallowRef<EventSource \| null> not shown
lastEventId ShallowRef<string \| null> not shown

Type Aliases

EventSourceStatus

type EventSourceStatus = 'CONNECTING' | 'OPEN' | 'CLOSED';

Generated by Syntax Scribe